From 0378d1733eb6b18daa19d7329cabc80c63db3442 Mon Sep 17 00:00:00 2001 From: Anvesha Saini <160641843+S3lc0uth@users.noreply.github.com> Date: Thu, 2 Oct 2025 18:53:12 +0000 Subject: [PATCH 1/3] Improve command docstrings for better help and markdown output --- dev/generate_cli_docs.py | 26 ++++++++++++-- python/cocoindex/cli.py | 74 +++++++++++++++++----------------------- 2 files changed, 56 insertions(+), 44 deletions(-) diff --git a/dev/generate_cli_docs.py b/dev/generate_cli_docs.py index 0ab2da13..64d67a74 100644 --- a/dev/generate_cli_docs.py +++ b/dev/generate_cli_docs.py @@ -58,6 +58,22 @@ def escape_html_tags(text: str) -> str: return "".join(result) +def backquote_terms(text: str) -> str: + """ + Wrap code-style terms (ALL_CAPS, example module names, file paths, angle brackets, .Name) in backticks. + """ + # Backquote ALL_CAPS terms + text = re.sub(r"\b([A-Z_]{2,})\b", r"`\1`", text) + # Backquote example modules and paths (simple heuristics) + text = re.sub(r"\b([a-zA-Z0-9_\.]+\.py)\b", r"`\1`", text) + text = re.sub(r"\b([a-zA-Z0-9_]+\.[a-zA-Z0-9_]+)\b", r"`\1`", text) + # Backquote things like :SpecificFlowName, :AnythingName + text = re.sub(r"(:[a-zA-Z0-9_]+Name\b)", r"`\1`", text) + # Backquote things in angle brackets (e.g., ) + text = re.sub(r"(<[A-Z_]+>)", r"`\1`", text) + return text + + def format_options_section(help_text: str) -> str: """Extract and format the options section.""" lines = help_text.split("\n") @@ -92,6 +108,7 @@ def format_options_section(help_text: str) -> str: # Save previous option if exists if current_option is not None: desc = " ".join(current_description).strip() + desc = backquote_terms(desc) desc = escape_html_tags(desc) # Escape HTML tags for MDX compatibility formatted_options.append(f"| `{current_option}` | {desc} |") @@ -118,6 +135,7 @@ def format_options_section(help_text: str) -> str: # Add last option if current_option is not None: desc = " ".join(current_description).strip() + desc = backquote_terms(desc) desc = escape_html_tags(desc) # Escape HTML tags for MDX compatibility formatted_options.append(f"| `{current_option}` | {desc} |") @@ -156,6 +174,7 @@ def format_commands_section(help_text: str) -> str: if match: command = match.group(1) description = match.group(2).strip() + description = backquote_terms(description) # Truncate long descriptions if len(description) > 80: description = description[:77] + "..." @@ -185,7 +204,10 @@ def extract_description(help_text: str) -> str: elif in_description and line.strip(): description_lines.append(line.strip()) - description = "\n\n".join(description_lines) if description_lines else "" + # Collapse multiple blank lines into a single blank line + description = "\n".join(description_lines) if description_lines else "" + description = re.sub(r"\n{2,}", "\n", description) + description = backquote_terms(description) return escape_html_tags(description) # Escape HTML tags for MDX compatibility @@ -283,4 +305,4 @@ def main() -> None: if __name__ == "__main__": - main() + main() \ No newline at end of file diff --git a/python/cocoindex/cli.py b/python/cocoindex/cli.py index 3e9e5bee..04f8df10 100644 --- a/python/cocoindex/cli.py +++ b/python/cocoindex/cli.py @@ -59,7 +59,7 @@ def _get_app_ref_from_specifier( specifier: str, ) -> str: """ - Parses the APP_TARGET to get the application reference (path or module). + Parses the `APP_TARGET` to get the application reference (path or module). Issues a warning if a flow name component is also provided in it. """ app_ref, flow_ref = _parse_app_flow_specifier(specifier) @@ -84,7 +84,7 @@ def _load_user_app(app_target: str) -> None: load_user_app(app_target) except UserAppLoaderError as e: raise click.ClickException( - f"Failed to load APP_TARGET '{app_target}': {e}" + f"Failed to load `APP_TARGET` '{app_target}': {e}" ) from e add_user_app(app_target) @@ -137,11 +137,8 @@ def ls(app_target: str | None) -> None: """ List all flows. - If APP_TARGET (path/to/app.py or a module) is provided, lists flows - defined in the app and their backend setup status. - - If APP_TARGET is omitted, lists all flows that have a persisted - setup in the backend. + If `APP_TARGET` (`path/to/app.py` or a module) is provided, lists flows defined in the app and their backend setup status. + If `APP_TARGET` is omitted, lists all flows that have a persisted setup in the backend. """ persisted_flow_names = flow_names_with_setup() if app_target: @@ -189,16 +186,13 @@ def show(app_flow_specifier: str, color: bool, verbose: bool) -> None: """ Show the flow spec and schema. - APP_FLOW_SPECIFIER: Specifies the application and optionally the target flow. + `APP_FLOW_SPECIFIER`: Specifies the application and optionally the target flow. Can be one of the following formats: - - \b - - path/to/your_app.py - - an_installed.module_name - - path/to/your_app.py:SpecificFlowName - - an_installed.module_name:SpecificFlowName - - :SpecificFlowName can be omitted only if the application defines a single flow. + - `path/to/your_app.py` + - `an_installed.module_name` + - `path/to/your_app.py:SpecificFlowName` + - `an_installed.module_name:SpecificFlowName` + `:SpecificFlowName` can be omitted only if the application defines a single flow. """ app_ref, flow_ref = _parse_app_flow_specifier(app_flow_specifier) _load_user_app(app_ref) @@ -223,7 +217,7 @@ def show(app_flow_specifier: str, color: bool, verbose: bool) -> None: def _drop_flows(flows: Iterable[flow.Flow], force: bool = False) -> None: """ Helper function to drop flows without user interaction. - Used internally by --reset flag + Used internally by `--reset` flag. Args: flows: Iterable of Flow objects to drop @@ -297,18 +291,18 @@ async def _update_all_flows_with_hint_async( is_flag=True, show_default=True, default=False, - help="Drop existing setup before running setup (equivalent to running 'cocoindex drop' first).", + help="Drop existing setup before running setup (equivalent to running `cocoindex drop` first).", ) def setup(app_target: str, force: bool, reset: bool) -> None: """ Check and apply backend setup changes for flows, including the internal storage and target (to export to). - APP_TARGET: path/to/app.py or installed_module. + `APP_TARGET`: `path/to/app.py` or `installed_module`. """ app_ref = _get_app_ref_from_specifier(app_target) _load_user_app(app_ref) - # If --reset is specified, drop existing setup first + # If `--reset` is specified, drop existing setup first if reset: _drop_flows(flow.flows().values(), force=force) @@ -330,7 +324,6 @@ def drop(app_target: str | None, flow_name: tuple[str, ...], force: bool) -> Non """ Drop the backend setup for flows. - \b Modes of operation: 1. Drop all flows defined in an app: `cocoindex drop ` 2. Drop specific named flows: `cocoindex drop [FLOW_NAME...]` @@ -339,8 +332,7 @@ def drop(app_target: str | None, flow_name: tuple[str, ...], force: bool) -> Non if not app_target: raise click.UsageError( - "Missing arguments. You must either provide an APP_TARGET (to target app-specific flows) " - "or use the --all flag." + "Missing arguments. You must either provide an `APP_TARGET` (to target app-specific flows) or use the `--all` flag." ) 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 is_flag=True, show_default=True, default=False, - help="Drop existing setup before updating (equivalent to running 'cocoindex drop' first).", + help="Drop existing setup before updating (equivalent to running `cocoindex drop` first).", ) @click.option( "-f", @@ -445,13 +437,13 @@ def update( """ Update the index to reflect the latest data from data sources. - APP_FLOW_SPECIFIER: path/to/app.py, module, path/to/app.py:FlowName, or module:FlowName. - If :FlowName is omitted, updates all flows. + `APP_FLOW_SPECIFIER`: `path/to/app.py`, `module`, `path/to/app.py:FlowName`, or `module:FlowName`. + If `:FlowName` is omitted, updates all flows. """ app_ref, flow_name = _parse_app_flow_specifier(app_flow_specifier) _load_user_app(app_ref) - # If --reset is specified, drop existing setup first + # If `--reset` is specified, drop existing setup first if reset: if flow_name: # Reset specific flow only @@ -514,15 +506,13 @@ def evaluate( Instead of updating the index, it dumps what should be indexed to files. Mainly used for evaluation purpose. - \b - APP_FLOW_SPECIFIER: Specifies the application and optionally the target flow. + `APP_FLOW_SPECIFIER`: Specifies the application and optionally the target flow. Can be one of the following formats: - - path/to/your_app.py - - an_installed.module_name - - path/to/your_app.py:SpecificFlowName - - an_installed.module_name:SpecificFlowName - - :SpecificFlowName can be omitted only if the application defines a single flow. + - `path/to/your_app.py` + - `an_installed.module_name` + - `path/to/your_app.py:SpecificFlowName` + - `an_installed.module_name:SpecificFlowName` + `:SpecificFlowName` can be omitted only if the application defines a single flow. """ app_ref, flow_ref = _parse_app_flow_specifier(app_flow_specifier) _load_user_app(app_ref) @@ -541,7 +531,7 @@ def evaluate( "--address", type=str, help="The address to bind the server to, in the format of IP:PORT. " - "If unspecified, the address specified in COCOINDEX_SERVER_ADDRESS will be used.", + "If unspecified, the address specified in `COCOINDEX_SERVER_ADDRESS` will be used.", ) @click.option( "-c", @@ -550,7 +540,7 @@ def evaluate( help="The origins of the clients (e.g. CocoInsight UI) to allow CORS from. " "Multiple origins can be specified as a comma-separated list. " "e.g. `https://cocoindex.io,http://localhost:3000`. " - "Origins specified in COCOINDEX_SERVER_CORS_ORIGINS will also be included.", + "Origins specified in `COCOINDEX_SERVER_CORS_ORIGINS` will also be included.", ) @click.option( "-ci", @@ -564,7 +554,7 @@ def evaluate( "-cl", "--cors-local", type=int, - help="Allow http://localhost: to access the server.", + help="Allow `http://localhost:` to access the server.", ) @click.option( "-L", @@ -586,7 +576,7 @@ def evaluate( is_flag=True, show_default=True, default=False, - help="Drop existing setup before starting server (equivalent to running 'cocoindex drop' first).", + help="Drop existing setup before starting server (equivalent to running `cocoindex drop` first).", ) @click.option( "--reexport", @@ -638,7 +628,7 @@ def server( It will allow tools like CocoInsight to access the server. - APP_TARGET: path/to/app.py or installed_module. + `APP_TARGET`: `path/to/app.py` or `installed_module`. """ app_ref = _get_app_ref_from_specifier(app_target) args = ( @@ -726,7 +716,7 @@ def _run_server( ) raise click.Abort() - # If --reset is specified, drop existing setup first + # If `--reset` is specified, drop existing setup first if run_reset: _drop_flows(flow.flows().values(), force=force) @@ -821,4 +811,4 @@ def _flow_by_name(name: str | None) -> flow.Flow: if __name__ == "__main__": - cli() + cli() \ No newline at end of file From 04f1d925c9137462b13d2bcb4dbfac3d25778b86 Mon Sep 17 00:00:00 2001 From: Anvesha Saini <160641843+S3lc0uth@users.noreply.github.com> Date: Fri, 3 Oct 2025 03:06:17 +0530 Subject: [PATCH 2/3] Improve help text formatting in CLI options Added \b --- python/cocoindex/cli.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/python/cocoindex/cli.py b/python/cocoindex/cli.py index 04f8df10..c9dac8e1 100644 --- a/python/cocoindex/cli.py +++ b/python/cocoindex/cli.py @@ -185,9 +185,10 @@ def ls(app_target: str | None) -> None: def show(app_flow_specifier: str, color: bool, verbose: bool) -> None: """ Show the flow spec and schema. - + `APP_FLOW_SPECIFIER`: Specifies the application and optionally the target flow. Can be one of the following formats: + \b - `path/to/your_app.py` - `an_installed.module_name` - `path/to/your_app.py:SpecificFlowName` @@ -323,7 +324,7 @@ def setup(app_target: str, force: bool, reset: bool) -> None: def drop(app_target: str | None, flow_name: tuple[str, ...], force: bool) -> None: """ Drop the backend setup for flows. - + \b Modes of operation: 1. Drop all flows defined in an app: `cocoindex drop ` 2. Drop specific named flows: `cocoindex drop [FLOW_NAME...]` @@ -505,7 +506,7 @@ def evaluate( Instead of updating the index, it dumps what should be indexed to files. Mainly used for evaluation purpose. - + \b `APP_FLOW_SPECIFIER`: Specifies the application and optionally the target flow. Can be one of the following formats: - `path/to/your_app.py` @@ -811,4 +812,4 @@ def _flow_by_name(name: str | None) -> flow.Flow: if __name__ == "__main__": - cli() \ No newline at end of file + cli() From 283012aa8fd4a189167a3d3be351e8e0695db450 Mon Sep 17 00:00:00 2001 From: Anvesha Saini <160641843+S3lc0uth@users.noreply.github.com> Date: Fri, 3 Oct 2025 03:12:09 +0530 Subject: [PATCH 3/3] Remove backquote_terms function and its references Removed the backquote_terms function and its usage in formatting options and descriptions. --- dev/generate_cli_docs.py | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/dev/generate_cli_docs.py b/dev/generate_cli_docs.py index 64d67a74..e6d317a0 100644 --- a/dev/generate_cli_docs.py +++ b/dev/generate_cli_docs.py @@ -57,23 +57,6 @@ def escape_html_tags(text: str) -> str: return "".join(result) - -def backquote_terms(text: str) -> str: - """ - Wrap code-style terms (ALL_CAPS, example module names, file paths, angle brackets, .Name) in backticks. - """ - # Backquote ALL_CAPS terms - text = re.sub(r"\b([A-Z_]{2,})\b", r"`\1`", text) - # Backquote example modules and paths (simple heuristics) - text = re.sub(r"\b([a-zA-Z0-9_\.]+\.py)\b", r"`\1`", text) - text = re.sub(r"\b([a-zA-Z0-9_]+\.[a-zA-Z0-9_]+)\b", r"`\1`", text) - # Backquote things like :SpecificFlowName, :AnythingName - text = re.sub(r"(:[a-zA-Z0-9_]+Name\b)", r"`\1`", text) - # Backquote things in angle brackets (e.g., ) - text = re.sub(r"(<[A-Z_]+>)", r"`\1`", text) - return text - - def format_options_section(help_text: str) -> str: """Extract and format the options section.""" lines = help_text.split("\n") @@ -108,7 +91,6 @@ def format_options_section(help_text: str) -> str: # Save previous option if exists if current_option is not None: desc = " ".join(current_description).strip() - desc = backquote_terms(desc) desc = escape_html_tags(desc) # Escape HTML tags for MDX compatibility formatted_options.append(f"| `{current_option}` | {desc} |") @@ -135,7 +117,6 @@ def format_options_section(help_text: str) -> str: # Add last option if current_option is not None: desc = " ".join(current_description).strip() - desc = backquote_terms(desc) desc = escape_html_tags(desc) # Escape HTML tags for MDX compatibility formatted_options.append(f"| `{current_option}` | {desc} |") @@ -174,7 +155,6 @@ def format_commands_section(help_text: str) -> str: if match: command = match.group(1) description = match.group(2).strip() - description = backquote_terms(description) # Truncate long descriptions if len(description) > 80: description = description[:77] + "..." @@ -206,8 +186,7 @@ def extract_description(help_text: str) -> str: # Collapse multiple blank lines into a single blank line description = "\n".join(description_lines) if description_lines else "" - description = re.sub(r"\n{2,}", "\n", description) - description = backquote_terms(description) + description = re.sub(r"\n{3,}", "\n\n", description) return escape_html_tags(description) # Escape HTML tags for MDX compatibility @@ -305,4 +284,4 @@ def main() -> None: if __name__ == "__main__": - main() \ No newline at end of file + main()