Skip to content

Commit 0378d17

Browse files
committed
Improve command docstrings for better help and markdown output
1 parent 624b233 commit 0378d17

File tree

2 files changed

+56
-44
lines changed

2 files changed

+56
-44
lines changed

dev/generate_cli_docs.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,22 @@ def escape_html_tags(text: str) -> str:
5858
return "".join(result)
5959

6060

61+
def backquote_terms(text: str) -> str:
62+
"""
63+
Wrap code-style terms (ALL_CAPS, example module names, file paths, angle brackets, .Name) in backticks.
64+
"""
65+
# Backquote ALL_CAPS terms
66+
text = re.sub(r"\b([A-Z_]{2,})\b", r"`\1`", text)
67+
# Backquote example modules and paths (simple heuristics)
68+
text = re.sub(r"\b([a-zA-Z0-9_\.]+\.py)\b", r"`\1`", text)
69+
text = re.sub(r"\b([a-zA-Z0-9_]+\.[a-zA-Z0-9_]+)\b", r"`\1`", text)
70+
# Backquote things like :SpecificFlowName, :AnythingName
71+
text = re.sub(r"(:[a-zA-Z0-9_]+Name\b)", r"`\1`", text)
72+
# Backquote things in angle brackets (e.g., <APP_TARGET>)
73+
text = re.sub(r"(<[A-Z_]+>)", r"`\1`", text)
74+
return text
75+
76+
6177
def format_options_section(help_text: str) -> str:
6278
"""Extract and format the options section."""
6379
lines = help_text.split("\n")
@@ -92,6 +108,7 @@ def format_options_section(help_text: str) -> str:
92108
# Save previous option if exists
93109
if current_option is not None:
94110
desc = " ".join(current_description).strip()
111+
desc = backquote_terms(desc)
95112
desc = escape_html_tags(desc) # Escape HTML tags for MDX compatibility
96113
formatted_options.append(f"| `{current_option}` | {desc} |")
97114

@@ -118,6 +135,7 @@ def format_options_section(help_text: str) -> str:
118135
# Add last option
119136
if current_option is not None:
120137
desc = " ".join(current_description).strip()
138+
desc = backquote_terms(desc)
121139
desc = escape_html_tags(desc) # Escape HTML tags for MDX compatibility
122140
formatted_options.append(f"| `{current_option}` | {desc} |")
123141

@@ -156,6 +174,7 @@ def format_commands_section(help_text: str) -> str:
156174
if match:
157175
command = match.group(1)
158176
description = match.group(2).strip()
177+
description = backquote_terms(description)
159178
# Truncate long descriptions
160179
if len(description) > 80:
161180
description = description[:77] + "..."
@@ -185,7 +204,10 @@ def extract_description(help_text: str) -> str:
185204
elif in_description and line.strip():
186205
description_lines.append(line.strip())
187206

188-
description = "\n\n".join(description_lines) if description_lines else ""
207+
# Collapse multiple blank lines into a single blank line
208+
description = "\n".join(description_lines) if description_lines else ""
209+
description = re.sub(r"\n{2,}", "\n", description)
210+
description = backquote_terms(description)
189211
return escape_html_tags(description) # Escape HTML tags for MDX compatibility
190212

191213

@@ -283,4 +305,4 @@ def main() -> None:
283305

284306

285307
if __name__ == "__main__":
286-
main()
308+
main()

python/cocoindex/cli.py

Lines changed: 32 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ def _get_app_ref_from_specifier(
5959
specifier: str,
6060
) -> str:
6161
"""
62-
Parses the APP_TARGET to get the application reference (path or module).
62+
Parses the `APP_TARGET` to get the application reference (path or module).
6363
Issues a warning if a flow name component is also provided in it.
6464
"""
6565
app_ref, flow_ref = _parse_app_flow_specifier(specifier)
@@ -84,7 +84,7 @@ def _load_user_app(app_target: str) -> None:
8484
load_user_app(app_target)
8585
except UserAppLoaderError as e:
8686
raise click.ClickException(
87-
f"Failed to load APP_TARGET '{app_target}': {e}"
87+
f"Failed to load `APP_TARGET` '{app_target}': {e}"
8888
) from e
8989

9090
add_user_app(app_target)
@@ -137,11 +137,8 @@ def ls(app_target: str | None) -> None:
137137
"""
138138
List all flows.
139139
140-
If APP_TARGET (path/to/app.py or a module) is provided, lists flows
141-
defined in the app and their backend setup status.
142-
143-
If APP_TARGET is omitted, lists all flows that have a persisted
144-
setup in the backend.
140+
If `APP_TARGET` (`path/to/app.py` or a module) is provided, lists flows defined in the app and their backend setup status.
141+
If `APP_TARGET` is omitted, lists all flows that have a persisted setup in the backend.
145142
"""
146143
persisted_flow_names = flow_names_with_setup()
147144
if app_target:
@@ -189,16 +186,13 @@ def show(app_flow_specifier: str, color: bool, verbose: bool) -> None:
189186
"""
190187
Show the flow spec and schema.
191188
192-
APP_FLOW_SPECIFIER: Specifies the application and optionally the target flow.
189+
`APP_FLOW_SPECIFIER`: Specifies the application and optionally the target flow.
193190
Can be one of the following formats:
194-
195-
\b
196-
- path/to/your_app.py
197-
- an_installed.module_name
198-
- path/to/your_app.py:SpecificFlowName
199-
- an_installed.module_name:SpecificFlowName
200-
201-
:SpecificFlowName can be omitted only if the application defines a single flow.
191+
- `path/to/your_app.py`
192+
- `an_installed.module_name`
193+
- `path/to/your_app.py:SpecificFlowName`
194+
- `an_installed.module_name:SpecificFlowName`
195+
`:SpecificFlowName` can be omitted only if the application defines a single flow.
202196
"""
203197
app_ref, flow_ref = _parse_app_flow_specifier(app_flow_specifier)
204198
_load_user_app(app_ref)
@@ -223,7 +217,7 @@ def show(app_flow_specifier: str, color: bool, verbose: bool) -> None:
223217
def _drop_flows(flows: Iterable[flow.Flow], force: bool = False) -> None:
224218
"""
225219
Helper function to drop flows without user interaction.
226-
Used internally by --reset flag
220+
Used internally by `--reset` flag.
227221
228222
Args:
229223
flows: Iterable of Flow objects to drop
@@ -297,18 +291,18 @@ async def _update_all_flows_with_hint_async(
297291
is_flag=True,
298292
show_default=True,
299293
default=False,
300-
help="Drop existing setup before running setup (equivalent to running 'cocoindex drop' first).",
294+
help="Drop existing setup before running setup (equivalent to running `cocoindex drop` first).",
301295
)
302296
def setup(app_target: str, force: bool, reset: bool) -> None:
303297
"""
304298
Check and apply backend setup changes for flows, including the internal storage and target (to export to).
305299
306-
APP_TARGET: path/to/app.py or installed_module.
300+
`APP_TARGET`: `path/to/app.py` or `installed_module`.
307301
"""
308302
app_ref = _get_app_ref_from_specifier(app_target)
309303
_load_user_app(app_ref)
310304

311-
# If --reset is specified, drop existing setup first
305+
# If `--reset` is specified, drop existing setup first
312306
if reset:
313307
_drop_flows(flow.flows().values(), force=force)
314308

@@ -330,7 +324,6 @@ def drop(app_target: str | None, flow_name: tuple[str, ...], force: bool) -> Non
330324
"""
331325
Drop the backend setup for flows.
332326
333-
\b
334327
Modes of operation:
335328
1. Drop all flows defined in an app: `cocoindex drop <APP_TARGET>`
336329
2. Drop specific named flows: `cocoindex drop <APP_TARGET> [FLOW_NAME...]`
@@ -339,8 +332,7 @@ def drop(app_target: str | None, flow_name: tuple[str, ...], force: bool) -> Non
339332

340333
if not app_target:
341334
raise click.UsageError(
342-
"Missing arguments. You must either provide an APP_TARGET (to target app-specific flows) "
343-
"or use the --all flag."
335+
"Missing arguments. You must either provide an `APP_TARGET` (to target app-specific flows) or use the `--all` flag."
344336
)
345337

346338
app_ref = _get_app_ref_from_specifier(app_target)
@@ -415,7 +407,7 @@ def drop(app_target: str | None, flow_name: tuple[str, ...], force: bool) -> Non
415407
is_flag=True,
416408
show_default=True,
417409
default=False,
418-
help="Drop existing setup before updating (equivalent to running 'cocoindex drop' first).",
410+
help="Drop existing setup before updating (equivalent to running `cocoindex drop` first).",
419411
)
420412
@click.option(
421413
"-f",
@@ -445,13 +437,13 @@ def update(
445437
"""
446438
Update the index to reflect the latest data from data sources.
447439
448-
APP_FLOW_SPECIFIER: path/to/app.py, module, path/to/app.py:FlowName, or module:FlowName.
449-
If :FlowName is omitted, updates all flows.
440+
`APP_FLOW_SPECIFIER`: `path/to/app.py`, `module`, `path/to/app.py:FlowName`, or `module:FlowName`.
441+
If `:FlowName` is omitted, updates all flows.
450442
"""
451443
app_ref, flow_name = _parse_app_flow_specifier(app_flow_specifier)
452444
_load_user_app(app_ref)
453445

454-
# If --reset is specified, drop existing setup first
446+
# If `--reset` is specified, drop existing setup first
455447
if reset:
456448
if flow_name:
457449
# Reset specific flow only
@@ -514,15 +506,13 @@ def evaluate(
514506
Instead of updating the index, it dumps what should be indexed to files.
515507
Mainly used for evaluation purpose.
516508
517-
\b
518-
APP_FLOW_SPECIFIER: Specifies the application and optionally the target flow.
509+
`APP_FLOW_SPECIFIER`: Specifies the application and optionally the target flow.
519510
Can be one of the following formats:
520-
- path/to/your_app.py
521-
- an_installed.module_name
522-
- path/to/your_app.py:SpecificFlowName
523-
- an_installed.module_name:SpecificFlowName
524-
525-
:SpecificFlowName can be omitted only if the application defines a single flow.
511+
- `path/to/your_app.py`
512+
- `an_installed.module_name`
513+
- `path/to/your_app.py:SpecificFlowName`
514+
- `an_installed.module_name:SpecificFlowName`
515+
`:SpecificFlowName` can be omitted only if the application defines a single flow.
526516
"""
527517
app_ref, flow_ref = _parse_app_flow_specifier(app_flow_specifier)
528518
_load_user_app(app_ref)
@@ -541,7 +531,7 @@ def evaluate(
541531
"--address",
542532
type=str,
543533
help="The address to bind the server to, in the format of IP:PORT. "
544-
"If unspecified, the address specified in COCOINDEX_SERVER_ADDRESS will be used.",
534+
"If unspecified, the address specified in `COCOINDEX_SERVER_ADDRESS` will be used.",
545535
)
546536
@click.option(
547537
"-c",
@@ -550,7 +540,7 @@ def evaluate(
550540
help="The origins of the clients (e.g. CocoInsight UI) to allow CORS from. "
551541
"Multiple origins can be specified as a comma-separated list. "
552542
"e.g. `https://cocoindex.io,http://localhost:3000`. "
553-
"Origins specified in COCOINDEX_SERVER_CORS_ORIGINS will also be included.",
543+
"Origins specified in `COCOINDEX_SERVER_CORS_ORIGINS` will also be included.",
554544
)
555545
@click.option(
556546
"-ci",
@@ -564,7 +554,7 @@ def evaluate(
564554
"-cl",
565555
"--cors-local",
566556
type=int,
567-
help="Allow http://localhost:<port> to access the server.",
557+
help="Allow `http://localhost:<port>` to access the server.",
568558
)
569559
@click.option(
570560
"-L",
@@ -586,7 +576,7 @@ def evaluate(
586576
is_flag=True,
587577
show_default=True,
588578
default=False,
589-
help="Drop existing setup before starting server (equivalent to running 'cocoindex drop' first).",
579+
help="Drop existing setup before starting server (equivalent to running `cocoindex drop` first).",
590580
)
591581
@click.option(
592582
"--reexport",
@@ -638,7 +628,7 @@ def server(
638628
639629
It will allow tools like CocoInsight to access the server.
640630
641-
APP_TARGET: path/to/app.py or installed_module.
631+
`APP_TARGET`: `path/to/app.py` or `installed_module`.
642632
"""
643633
app_ref = _get_app_ref_from_specifier(app_target)
644634
args = (
@@ -726,7 +716,7 @@ def _run_server(
726716
)
727717
raise click.Abort()
728718

729-
# If --reset is specified, drop existing setup first
719+
# If `--reset` is specified, drop existing setup first
730720
if run_reset:
731721
_drop_flows(flow.flows().values(), force=force)
732722

@@ -821,4 +811,4 @@ def _flow_by_name(name: str | None) -> flow.Flow:
821811

822812

823813
if __name__ == "__main__":
824-
cli()
814+
cli()

0 commit comments

Comments
 (0)