Skip to content

Commit

Permalink
FEATURE: command line autocompletion for bash and zsh shells
Browse files Browse the repository at this point in the history
  • Loading branch information
amilcarlucas committed Feb 6, 2025
1 parent 0e1d068 commit aa992c5
Show file tree
Hide file tree
Showing 18 changed files with 119 additions and 53 deletions.
30 changes: 30 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ the following system design requirements were derived:
- [DevOps](https://en.wikipedia.org/wiki/DevOps)
- [CI/CD automation](https://en.wikipedia.org/wiki/CI/CD)
- git pre-commit hooks for code linting and other code quality checks
- create command-line autocompletion for bash, zsh and powershell [PR #134](https://github.com/ArduPilot/MethodicConfigurator/pull/134)

### The Software architecture

Expand Down Expand Up @@ -457,3 +458,32 @@ or create a gitlab Pull request with the changes to the `.po` file.
The github [robot will automatically convert that `.po` file into a `.mo` file](https://github.com/ArduPilot/MethodicConfigurator/actions/workflows/update_mo_files.yml)
and create an [*ArduPilot methodic configurator* installer](https://github.com/ArduPilot/MethodicConfigurator/actions/workflows/windows_build.yml)
that you can use to test the translations.

## Install command line completion

For Bash autocompletion, add this to your `~/.bashrc`:

```bash
eval "$(register-python-argcomplete ardupilot_methodic_configurator)"
```

For Zsh autocompletion, add these lines to your `~/.zshrc`:

```zsh
autoload -U bashcompinit
bashcompinit
eval "$(register-python-argcomplete ardupilot_methodic_configurator)"
```

For PowerShell autocompletion, run this command in PowerShell:

```powershell
Register-ArgumentCompleter -Native -CommandName ardupilot_methodic_configurator -ScriptBlock {
param($wordToComplete, $commandAst, $cursorPosition)
$env:COMP_LINE=$commandAst.ToString()
$env:COMP_POINT=$cursorPosition
ardupilot_methodic_configurator | ForEach-Object {
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
}
}
```
17 changes: 10 additions & 7 deletions ardupilot_methodic_configurator/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@
from typing import Union
from webbrowser import open as webbrowser_open

import argcomplete

from ardupilot_methodic_configurator import _, __version__
from ardupilot_methodic_configurator.backend_filesystem import LocalFilesystem
from ardupilot_methodic_configurator.backend_filesystem_program_settings import ProgramSettings
from ardupilot_methodic_configurator.backend_flightcontroller import FlightController
from ardupilot_methodic_configurator.common_arguments import add_common_arguments_and_parse
from ardupilot_methodic_configurator.common_arguments import add_common_arguments
from ardupilot_methodic_configurator.frontend_tkinter_base import show_error_message
from ardupilot_methodic_configurator.frontend_tkinter_component_editor import ComponentEditorWindow
from ardupilot_methodic_configurator.frontend_tkinter_connection_selection import ConnectionSelectionWindow
Expand All @@ -34,14 +36,12 @@
from ardupilot_methodic_configurator.middleware_software_updates import UpdateManager, check_for_software_updates


def argument_parser() -> argparse.Namespace:
def create_argument_parser() -> argparse.ArgumentParser:
"""
Parses command-line arguments for the script.
This function sets up an argument parser to handle the command-line arguments for the script.
Returns:
argparse.Namespace: An object containing the parsed arguments.
argparse.ArgumentParser: The argument parser object.
"""
parser = argparse.ArgumentParser(
Expand All @@ -63,7 +63,10 @@ def argument_parser() -> argparse.Namespace:
parser = ComponentEditorWindow.add_argparse_arguments(parser)
parser = ParameterEditorWindow.add_argparse_arguments(parser)
parser = UpdateManager.add_argparse_arguments(parser)
return add_common_arguments_and_parse(parser)
parser = add_common_arguments(parser)

argcomplete.autocomplete(parser)
return parser


def connect_to_fc_and_set_vehicle_type(args: argparse.Namespace) -> tuple[FlightController, str]:
Expand Down Expand Up @@ -134,7 +137,7 @@ def component_editor(


def main() -> None:
args = argument_parser()
args = create_argument_parser().parse_args()

logging_basicConfig(level=logging_getLevelName(args.loglevel), format="%(asctime)s - %(levelname)s - %(message)s")

Expand Down
12 changes: 10 additions & 2 deletions ardupilot_methodic_configurator/annotate_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
from typing import Any, Optional, Union
from xml.etree import ElementTree as ET # no parsing, just data-structure manipulation

import argcomplete
from defusedxml import ElementTree as DET # noqa: N814, just parsing, no data-structure manipulation

# URL of the XML file
Expand All @@ -52,7 +53,7 @@
# mypy: disable-error-code="unused-ignore"


def arg_parser() -> argparse.Namespace:
def create_argument_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
description="Fetches on-line ArduPilot parameter documentation and adds it to the "
"specified file or to all *.param and *.parm files in the specified directory."
Expand Down Expand Up @@ -107,6 +108,13 @@ def arg_parser() -> argparse.Namespace:
help="Display version information and exit.",
)

argcomplete.autocomplete(parser)
return parser


def parse_arguments() -> argparse.Namespace:
parser = create_argument_parser()

args = parser.parse_args()

if args.verbose:
Expand Down Expand Up @@ -818,7 +826,7 @@ def parse_parameter_metadata(


def main() -> None:
args = arg_parser()
args = parse_arguments()
try:
xml_url = get_xml_url(args.vehicle_type, args.firmware_version)
xml_dir = get_xml_dir(args.target)
Expand Down
10 changes: 6 additions & 4 deletions ardupilot_methodic_configurator/backend_mavftp.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@
import struct
import sys
import time
from argparse import ArgumentParser, Namespace
from argparse import ArgumentParser
from collections.abc import Generator
from datetime import datetime
from io import BufferedReader, BufferedWriter
from io import BytesIO as SIO # noqa: N814
from typing import Union

import argcomplete
from pymavlink import mavutil

# pylint: disable=too-many-lines
Expand Down Expand Up @@ -1397,7 +1398,7 @@ def decode_and_save_params(fh) -> MAVFTPReturn:

if __name__ == "__main__":

def argument_parser() -> Namespace:
def create_argument_parser() -> ArgumentParser:
"""
Parses command-line arguments for the script.
Expand Down Expand Up @@ -1527,7 +1528,8 @@ def argument_parser() -> Namespace:
parser_crc.add_argument("arg1", type=str, metavar="remote_path", help="Path to the file to calculate the CRC of.")

# Add other subparsers commands as needed
return parser.parse_args()
argcomplete.autocomplete(parser)
return parser

def auto_detect_serial() -> list[mavutil.SerialPort]:
preferred_ports = [
Expand Down Expand Up @@ -1592,7 +1594,7 @@ def wait_heartbeat(m) -> None:

def main() -> None:
"""For testing/example purposes only."""
args = argument_parser()
args = create_argument_parser().parse_args()

logging.basicConfig(level=logging.getLevelName(args.loglevel), format="%(levelname)s - %(message)s")

Expand Down
6 changes: 3 additions & 3 deletions ardupilot_methodic_configurator/common_arguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@
SPDX-License-Identifier: GPL-3.0-or-later
"""

from argparse import ArgumentParser, Namespace
from argparse import ArgumentParser

from ardupilot_methodic_configurator import _, __version__
from ardupilot_methodic_configurator.internationalization import LANGUAGE_CHOICES


def add_common_arguments_and_parse(parser: ArgumentParser) -> Namespace:
def add_common_arguments(parser: ArgumentParser) -> ArgumentParser:
parser.add_argument(
"--loglevel",
type=str,
Expand All @@ -32,4 +32,4 @@ def add_common_arguments_and_parse(parser: ArgumentParser) -> Namespace:
choices=LANGUAGE_CHOICES,
help=_("User interface language. Default is %(default)s."),
)
return parser.parse_args()
return parser
30 changes: 19 additions & 11 deletions ardupilot_methodic_configurator/extract_param_defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import re
from typing import Union

import argcomplete
from pymavlink import mavutil

NO_DEFAULT_VALUES_MESSAGE = "The .bin file contained no parameter default values. Update to a newer ArduPilot firmware version"
Expand All @@ -27,17 +28,7 @@
MAV_PARAM_TYPE_REAL32 = 9


def parse_arguments(args: Union[None, argparse.Namespace] = None) -> argparse.Namespace:
"""
Parses command line arguments for the script.
Args:
args: List of command line arguments. Default is None, which means it uses sys.argv.
Returns:
Namespace object containing the parsed arguments.
"""
def create_argument_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description="Extracts parameter default values from an ArduPilot .bin log file.")
parser.add_argument(
"-f",
Expand Down Expand Up @@ -82,6 +73,23 @@ def parse_arguments(args: Union[None, argparse.Namespace] = None) -> argparse.Na
help="Type of parameter values to extract. Default is %(default)s.",
)
parser.add_argument("bin_file", help="The ArduPilot .bin log file to read")

argcomplete.autocomplete(parser)
return parser


def parse_arguments(args: Union[None, argparse.Namespace] = None) -> argparse.Namespace:
"""
Parses command line arguments for the script.
Args:
args: List of command line arguments. Default is None, which means it uses sys.argv.
Returns:
Namespace object containing the parsed arguments.
"""
parser = create_argument_parser()
args, _ = parser.parse_known_args(args) # type: ignore[arg-type]

if args is None:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from ardupilot_methodic_configurator.backend_filesystem import LocalFilesystem
from ardupilot_methodic_configurator.backend_filesystem_vehicle_components import VehicleComponents
from ardupilot_methodic_configurator.battery_cell_voltages import BatteryCell
from ardupilot_methodic_configurator.common_arguments import add_common_arguments_and_parse
from ardupilot_methodic_configurator.common_arguments import add_common_arguments

# from ardupilot_methodic_configurator.frontend_tkinter_base import show_tooltip
from ardupilot_methodic_configurator.frontend_tkinter_base import show_error_message
Expand All @@ -52,7 +52,7 @@ def argument_parser() -> Namespace:
)
parser = LocalFilesystem.add_argparse_arguments(parser)
parser = ComponentEditorWindow.add_argparse_arguments(parser)
return add_common_arguments_and_parse(parser)
return add_common_arguments(parser).parse_args()
# pylint: enable=duplicate-code


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

from ardupilot_methodic_configurator import _, __version__
from ardupilot_methodic_configurator.backend_filesystem import LocalFilesystem
from ardupilot_methodic_configurator.common_arguments import add_common_arguments_and_parse
from ardupilot_methodic_configurator.common_arguments import add_common_arguments
from ardupilot_methodic_configurator.frontend_tkinter_base import (
BaseWindow,
RichText,
Expand Down Expand Up @@ -52,7 +52,7 @@ def argument_parser() -> Namespace:
)
parser = LocalFilesystem.add_argparse_arguments(parser)
parser = ComponentEditorWindowBase.add_argparse_arguments(parser)
return add_common_arguments_and_parse(parser)
return add_common_arguments(parser).parse_args()
# pylint: enable=duplicate-code


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

from ardupilot_methodic_configurator import _
from ardupilot_methodic_configurator.backend_flightcontroller import FlightController
from ardupilot_methodic_configurator.common_arguments import add_common_arguments_and_parse
from ardupilot_methodic_configurator.common_arguments import add_common_arguments
from ardupilot_methodic_configurator.frontend_tkinter_base import (
BaseWindow,
ProgressWindow,
Expand Down Expand Up @@ -274,7 +274,7 @@ def argument_parser() -> Namespace:
)
)
parser = FlightController.add_argparse_arguments(parser)
return add_common_arguments_and_parse(parser)
return add_common_arguments(parser).parse_args()


# pylint: disable=duplicate-code
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from ardupilot_methodic_configurator import _, __version__
from ardupilot_methodic_configurator.backend_filesystem import LocalFilesystem
from ardupilot_methodic_configurator.backend_filesystem_program_settings import ProgramSettings
from ardupilot_methodic_configurator.common_arguments import add_common_arguments_and_parse
from ardupilot_methodic_configurator.common_arguments import add_common_arguments
from ardupilot_methodic_configurator.frontend_tkinter_base import BaseWindow, show_no_param_files_error, show_tooltip
from ardupilot_methodic_configurator.frontend_tkinter_template_overview import TemplateOverviewWindow

Expand Down Expand Up @@ -491,7 +491,7 @@ def argument_parser() -> Namespace:
)
)
parser = LocalFilesystem.add_argparse_arguments(parser)
return add_common_arguments_and_parse(parser)
return add_common_arguments(parser).parse_args()


# pylint: disable=duplicate-code
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from typing import Union

from ardupilot_methodic_configurator import _
from ardupilot_methodic_configurator.common_arguments import add_common_arguments_and_parse
from ardupilot_methodic_configurator.common_arguments import add_common_arguments
from ardupilot_methodic_configurator.frontend_tkinter_base import get_widget_font_family_and_size, update_combobox_width

# SPDX-SnippetBegin
Expand Down Expand Up @@ -230,7 +230,7 @@ def argument_parser() -> Namespace:
"Not to be used directly, but through the main ArduPilot methodic configurator script."
)
)
return add_common_arguments_and_parse(parser)
return add_common_arguments(parser).parse_args()


def main() -> None:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
from ardupilot_methodic_configurator.backend_filesystem_program_settings import ProgramSettings
from ardupilot_methodic_configurator.backend_flightcontroller import FlightController
from ardupilot_methodic_configurator.backend_internet import download_file_from_url
from ardupilot_methodic_configurator.common_arguments import add_common_arguments_and_parse
from ardupilot_methodic_configurator.common_arguments import add_common_arguments
from ardupilot_methodic_configurator.frontend_tkinter_base import (
AutoResizeCombobox,
BaseWindow,
Expand Down Expand Up @@ -832,7 +832,7 @@ def argument_parser() -> Namespace:
parser = FlightController.add_argparse_arguments(parser)
parser = LocalFilesystem.add_argparse_arguments(parser)
parser = ParameterEditorWindow.add_argparse_arguments(parser)
return add_common_arguments_and_parse(parser)
return add_common_arguments(parser).parse_args()


if __name__ == "__main__":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from ardupilot_methodic_configurator import _, __version__
from ardupilot_methodic_configurator.backend_filesystem_program_settings import ProgramSettings
from ardupilot_methodic_configurator.backend_filesystem_vehicle_components import VehicleComponents
from ardupilot_methodic_configurator.common_arguments import add_common_arguments_and_parse
from ardupilot_methodic_configurator.common_arguments import add_common_arguments
from ardupilot_methodic_configurator.frontend_tkinter_base import BaseWindow
from ardupilot_methodic_configurator.middleware_template_overview import TemplateOverview

Expand Down Expand Up @@ -225,7 +225,7 @@ def argument_parser() -> argparse.Namespace:
"providing a clear and intuitive interface for parameter management."
)
)
return add_common_arguments_and_parse(parser)
return add_common_arguments(parser).parse_args()


def main() -> None:
Expand Down
Loading

0 comments on commit aa992c5

Please sign in to comment.