|
1 | 1 | # (C) Datadog, Inc. 2020-present
|
2 | 2 | # All rights reserved
|
3 | 3 | # Licensed under the Apache license (see LICENSE)
|
4 |
| -from typing import Iterable, Iterator, List, Optional, cast |
| 4 | +from typing import Iterator, List, Optional, cast |
5 | 5 |
|
6 | 6 | import click
|
7 | 7 |
|
@@ -30,7 +30,7 @@ def _recursively_make_command_docs(
|
30 | 30 | subcommands = _get_sub_commands(ctx.command, ctx)
|
31 | 31 |
|
32 | 32 | for command in sorted(subcommands, key=lambda cmd: cmd.name):
|
33 |
| - yield from _recursively_make_command_docs(command.name, command, parent=ctx, level=level + 1) |
| 33 | + yield from _recursively_make_command_docs(command.name, command, parent=ctx, level=level + 1, style=style) |
34 | 34 |
|
35 | 35 |
|
36 | 36 | def _get_sub_commands(command: click.Command, ctx: click.Context) -> List[click.Command]:
|
@@ -135,42 +135,80 @@ def _make_plain_options(ctx: click.Context) -> Iterator[str]:
|
135 | 135 | yield ""
|
136 | 136 |
|
137 | 137 |
|
138 |
| -def _make_table_options(ctx: click.Context) -> Iterator[str]: |
139 |
| - """Create the table style options description.""" |
| 138 | +# Unicode "Vertical Line" character (U+007C), HTML-compatible. |
| 139 | +# "\|" (escaped pipe) would work, too, but linters don't like it in literals. |
| 140 | +# https://stackoverflow.com/questions/23723396/how-to-show-the-pipe-symbol-in-markdown-table |
| 141 | +_HTML_PIPE = "|" |
| 142 | + |
| 143 | + |
| 144 | +def _format_table_option_type(option: click.Option) -> str: |
| 145 | + typename = option.type.name |
| 146 | + |
| 147 | + # TODO: remove "# type: ignore" comments once https://github.com/python/typeshed/pull/4813 gets merged and released. |
140 | 148 |
|
141 |
| - def backquote(opts: Iterable[str]) -> List[str]: |
142 |
| - return [f"`{opt}`" for opt in opts] |
143 |
| - |
144 |
| - def format_possible_value(opt: click.Option) -> str: |
145 |
| - param_type = opt.type |
146 |
| - display_name = param_type.name |
147 |
| - |
148 |
| - # TODO: remove type-ignore comments once python/typeshed#4813 gets merged. |
149 |
| - if isinstance(param_type, click.Choice): |
150 |
| - return f"{display_name} ({' | '.join(backquote(param_type.choices))})" |
151 |
| - elif isinstance(param_type, click.DateTime): |
152 |
| - return f"{display_name} ({' | '.join(backquote(param_type.formats))})" # type: ignore[attr-defined] |
153 |
| - elif isinstance(param_type, (click.IntRange, click.FloatRange)): |
154 |
| - if param_type.min is not None and param_type.max is not None: # type: ignore[union-attr] |
155 |
| - return f"{display_name} (between `{param_type.min}` and `{param_type.max}`)" # type: ignore[union-attr] |
156 |
| - elif param_type.min is not None: # type: ignore[union-attr] |
157 |
| - return f"{display_name} (`{param_type.min}` and above)" # type: ignore[union-attr] |
158 |
| - else: |
159 |
| - return f"{display_name} (`{param_type.max}` and below)" # type: ignore[union-attr] |
| 149 | + if isinstance(option.type, click.Choice): |
| 150 | + # @click.option(..., type=click.Choice(["A", "B", "C"])) |
| 151 | + # -> choices (`A` | `B` | `C`) |
| 152 | + choices = f" {_HTML_PIPE} ".join(f"`{choice}`" for choice in option.type.choices) |
| 153 | + return f"{typename} ({choices})" |
| 154 | + |
| 155 | + if isinstance(option.type, click.DateTime): |
| 156 | + # @click.option(..., type=click.DateTime(["A", "B", "C"])) |
| 157 | + # -> datetime (`%Y-%m-%d` | `%Y-%m-%dT%H:%M:%S` | `%Y-%m-%d %H:%M:%S`) |
| 158 | + formats = f" {_HTML_PIPE} ".join(f"`{fmt}`" for fmt in option.type.formats) # type: ignore[attr-defined] |
| 159 | + return f"{typename} ({formats})" |
| 160 | + |
| 161 | + if isinstance(option.type, (click.IntRange, click.FloatRange)): |
| 162 | + if option.type.min is not None and option.type.max is not None: # type: ignore[union-attr] |
| 163 | + # @click.option(..., type=click.IntRange(min=0, max=10)) |
| 164 | + # -> integer range (between `0` and `10`) |
| 165 | + return f"{typename} (between `{option.type.min}` and `{option.type.max}`)" # type: ignore[union-attr] |
| 166 | + elif option.type.min is not None: # type: ignore[union-attr] |
| 167 | + # @click.option(..., type=click.IntRange(min=0)) |
| 168 | + # -> integer range (`0` and above) |
| 169 | + return f"{typename} (`{option.type.min}` and above)" # type: ignore[union-attr] |
160 | 170 | else:
|
161 |
| - return display_name |
| 171 | + # @click.option(..., type=click.IntRange(max=10)) |
| 172 | + # -> integer range (`10` and below) |
| 173 | + return f"{typename} (`{option.type.max}` and below)" # type: ignore[union-attr] |
| 174 | + |
| 175 | + # -> "boolean", "text", etc. |
| 176 | + return typename |
| 177 | + |
| 178 | + |
| 179 | +def _format_table_option_row(option: click.Option) -> str: |
| 180 | + # Example: @click.option("-V, --version/--show-version", is_flag=True, help="Show version info.") |
| 181 | + |
| 182 | + # -> "`-V`, `--version`" |
| 183 | + names = ", ".join(f"`{opt}`" for opt in option.opts) |
| 184 | + |
| 185 | + if option.secondary_opts: |
| 186 | + # -> "`-V`, `--version` / `--show-info`" |
| 187 | + names += " / " |
| 188 | + names += ", ".join(f"`{opt}`" for opt in option.secondary_opts) |
| 189 | + |
| 190 | + # -> "boolean" |
| 191 | + value_type = _format_table_option_type(option) |
| 192 | + |
| 193 | + # -> "Show version info." |
| 194 | + description = option.help if option.help is not None else "N/A" |
| 195 | + |
| 196 | + # -> `False` |
| 197 | + default = f"`{option.default}`" if option.default is not None else "_required_" |
| 198 | + |
| 199 | + # -> "| `-V`, `--version` / `--show-version` | boolean | Show version info. | `False` |" |
| 200 | + return f"| {names} | {value_type} | {description} | {default} |" |
| 201 | + |
| 202 | + |
| 203 | +def _make_table_options(ctx: click.Context) -> Iterator[str]: |
| 204 | + """Create the table style options description.""" |
162 | 205 |
|
163 |
| - params = [param for param in ctx.command.get_params(ctx) if isinstance(param, click.Option)] |
| 206 | + options = [param for param in ctx.command.get_params(ctx) if isinstance(param, click.Option)] |
| 207 | + option_rows = [_format_table_option_row(option) for option in options] |
164 | 208 |
|
165 | 209 | yield "Options:"
|
166 | 210 | yield ""
|
167 | 211 | yield "| Name | Type | Description | Default |"
|
168 |
| - yield "| ------ | ---- | ----------- | ------- |" |
169 |
| - for param in params: |
170 |
| - names = ", ".join(backquote(param.opts)) |
171 |
| - names_negation = f" / {', '.join(backquote(param.secondary_opts))}" if param.secondary_opts != [] else "" |
172 |
| - value_type = format_possible_value(param) |
173 |
| - description = param.help if param.help is not None else "N/A" |
174 |
| - default = f"`{param.default}`" if param.default is not None else "_required_" |
175 |
| - yield f"| {names}{names_negation} | {value_type} | {description} | {default} |" |
| 212 | + yield "| ---- | ---- | ----------- | ------- |" |
| 213 | + yield from option_rows |
176 | 214 | yield ""
|
0 commit comments