From ef5a173db0b5edc85ca8ef7e7879b9d04d189d6b Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 4 Mar 2024 16:15:46 +0530 Subject: [PATCH] fix: misleading CLI error message for missing command (#25049) --- frappe/tests/test_commands.py | 7 +++++++ frappe/utils/bench_helper.py | 20 +++++++++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/frappe/tests/test_commands.py b/frappe/tests/test_commands.py index 56859c8126..ea9fc661fe 100644 --- a/frappe/tests/test_commands.py +++ b/frappe/tests/test_commands.py @@ -1010,3 +1010,10 @@ class TestSchedulerCLI(BaseTestCommands): self.execute("bench --site {site} scheduler resume") self.assertEqual(self.returncode, 0) self.assertRegex(self.stdout, r"Scheduler is resumed for site .*") + + +class TestCLIImplementation(BaseTestCommands): + def test_missing_commands(self): + self.execute("bench --site {site} migrat") + self.assertNotEqual(self.returncode, 0) + self.assertRegex(self.stderr, r"No such.*migrat.*migrate") diff --git a/frappe/utils/bench_helper.py b/frappe/utils/bench_helper.py index 474010822b..273b5be128 100644 --- a/frappe/utils/bench_helper.py +++ b/frappe/utils/bench_helper.py @@ -3,7 +3,6 @@ import json import os import traceback import warnings -from pathlib import Path from textwrap import dedent import click @@ -14,10 +13,25 @@ import frappe.utils click.disable_unicode_literals_warning = True +class FrappeCommandGroup(click.Group): + def get_command(self, ctx, cmd_name): + rv = super().get_command(ctx, cmd_name) + if rv is not None: + return rv + + all_commands = self.list_commands(ctx) + from difflib import get_close_matches + + possibilities = get_close_matches(cmd_name, all_commands) + raise click.NoSuchOption( + cmd_name, possibilities=possibilities, message=f"No such command: {cmd_name}" + ) + + def main(): commands = get_app_groups() commands.update({"get-frappe-commands": get_frappe_commands, "get-frappe-help": get_frappe_help}) - click.Group(commands=commands)(prog_name="bench") + FrappeCommandGroup(commands=commands)(prog_name="bench") def get_app_groups() -> dict[str, click.Group]: @@ -27,7 +41,7 @@ def get_app_groups() -> dict[str, click.Group]: for app in get_apps(): if app_commands := get_app_commands(app): commands |= app_commands - return dict(frappe=click.group(name="frappe", commands=commands)(app_group)) + return dict(frappe=click.group(name="frappe", commands=commands, cls=FrappeCommandGroup)(app_group)) def get_app_group(app: str) -> click.Group: