|
8 | 8 |
|
9 | 9 | import curses |
10 | 10 |
|
| 11 | +from docstring_parser.common import DocstringExample |
| 12 | + |
11 | 13 | from recline.arg_types.positional import Positional |
12 | 14 | from recline.arg_types.remainder import Remainder |
13 | 15 | from recline.commands.cli_command import get_annotation_type |
14 | 16 |
|
15 | 17 |
|
| 18 | +def _iter_examples(docstring): |
| 19 | + """Yield (name, description) tuples from a parsed docstring. |
| 20 | +
|
| 21 | + Handles two input forms: |
| 22 | + - ``DocstringExample`` objects (google-style ``Examples:`` blocks), whose |
| 23 | + ``description`` field contains lines like ``name:`` followed by indented |
| 24 | + body lines. |
| 25 | + - Generic ``DocstringMeta`` entries with ``args[0]`` in ``{"example", |
| 26 | + "examples"}`` and the example name in ``args[1]`` (rest-style ``:example |
| 27 | + name:`` entries). |
| 28 | + """ |
| 29 | + |
| 30 | + for meta in docstring.meta: |
| 31 | + if isinstance(meta, DocstringExample): |
| 32 | + name = None |
| 33 | + body_lines = [] |
| 34 | + for line in (meta.description or '').splitlines(): |
| 35 | + stripped = line.rstrip() |
| 36 | + if stripped and not line[:1].isspace() and stripped.endswith(':'): |
| 37 | + if name is not None: |
| 38 | + yield name, '\n'.join(body_lines).strip('\n') |
| 39 | + name = stripped[:-1] |
| 40 | + body_lines = [] |
| 41 | + else: |
| 42 | + dedented = line[4:] if line.startswith(' ') else line |
| 43 | + body_lines.append(dedented) |
| 44 | + if name is not None: |
| 45 | + yield name, '\n'.join(body_lines).strip('\n') |
| 46 | + elif meta.args and meta.args[0] in {'example', 'examples'} and len(meta.args) > 1: |
| 47 | + yield meta.args[1], meta.description or '' |
| 48 | + |
| 49 | + |
16 | 50 | def wrapped_string(text, screen_width, prefix=0): |
17 | 51 | """This function will take a string and make sure it can fit within the |
18 | 52 | given screen_width. |
@@ -111,7 +145,7 @@ def generate_help_text(screen_width, command_class): |
111 | 145 | help_text.append(('\n',)) |
112 | 146 |
|
113 | 147 | # each command parameter with description, constraints, and defaults |
114 | | - if command_class.docstring.params: |
| 148 | + if command_class.required_args or command_class.optional_args: |
115 | 149 | def print_arg(arg): |
116 | 150 | meta = command_class.get_arg_metavar(arg) |
117 | 151 | description = command_class.get_arg_description(arg, indent=None) |
@@ -139,15 +173,16 @@ def print_arg(arg): |
139 | 173 | print_arg(arg) |
140 | 174 |
|
141 | 175 | # each command example |
142 | | - if command_class.docstring.examples: |
| 176 | + examples = list(_iter_examples(command_class.docstring)) |
| 177 | + if examples: |
143 | 178 | help_text.append(('EXAMPLES\n', curses.A_BOLD)) |
144 | | - for example in command_class.docstring.examples: |
| 179 | + for name, description in examples: |
145 | 180 | prefix = indent + ' ' |
146 | 181 | help_text.append((indent,)) |
147 | | - help_text.append((f'{example.name}:', curses.A_UNDERLINE)) |
| 182 | + help_text.append((f'{name}:', curses.A_UNDERLINE)) |
148 | 183 | help_text.append((f'\n{prefix}',)) |
149 | 184 | description = wrapped_string( |
150 | | - example.description, screen_width, prefix=len(prefix), |
| 185 | + description, screen_width, prefix=len(prefix), |
151 | 186 | ) |
152 | 187 | for line in description.split('\n'): |
153 | 188 | help_text.append((f'{line}\n',)) |
|
0 commit comments