Skip to content

Commit 3a197ce

Browse files
committed
feat(args): add command line argument completion.
Add tests to prevent regressions
1 parent a4f1233 commit 3a197ce

25 files changed

+649
-295
lines changed

ARCHITECTURE.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ the following system design requirements were derived:
125125
- [DevOps](https://en.wikipedia.org/wiki/DevOps)
126126
- [CI/CD automation](https://en.wikipedia.org/wiki/CI/CD)
127127
- git pre-commit hooks for code linting and other code quality checks
128+
- create command-line autocompletion for bash, zsh and powershell [PR #134](https://github.com/ArduPilot/MethodicConfigurator/pull/134)
128129

129130
### The Software architecture
130131

@@ -467,3 +468,65 @@ or create a gitlab Pull request with the changes to the `.po` file.
467468
The github [robot will automatically convert that `.po` file into a `.mo` file](https://github.com/ArduPilot/MethodicConfigurator/actions/workflows/update_mo_files.yml)
468469
and create an [*ArduPilot methodic configurator* installer](https://github.com/ArduPilot/MethodicConfigurator/actions/workflows/windows_build.yml)
469470
that you can use to test the translations.
471+
472+
## Install command line completion
473+
474+
### Global python argcomplete
475+
476+
For command line (tab) completion for all python scripts that support [argcomplete](https://github.com/kislyuk/argcomplete) do:
477+
478+
```bash
479+
activate-global-python-argcomplete
480+
```
481+
482+
### Fine granular python argcomplete
483+
484+
For Bash autocompletion, add this to your `~/.bashrc`:
485+
486+
```bash
487+
eval "$(register-python-argcomplete ardupilot_methodic_configurator)"
488+
eval "$(register-python-argcomplete extract_param_defaults)"
489+
eval "$(register-python-argcomplete annotate_params)"
490+
eval "$(register-python-argcomplete param_pid_adjustment_update)"
491+
eval "$(register-python-argcomplete mavftp)"
492+
```
493+
494+
For Zsh autocompletion, add these lines to your `~/.zshrc`:
495+
496+
```zsh
497+
autoload -U bashcompinit
498+
bashcompinit
499+
eval "$(register-python-argcomplete ardupilot_methodic_configurator)"
500+
eval "$(register-python-argcomplete extract_param_defaults)"
501+
eval "$(register-python-argcomplete annotate_params)"
502+
eval "$(register-python-argcomplete param_pid_adjustment_update)"
503+
eval "$(register-python-argcomplete mavftp)"
504+
```
505+
506+
For PowerShell autocompletion, run this command in PowerShell:
507+
508+
```powershell
509+
$scripts = @(
510+
'ardupilot_methodic_configurator',
511+
'extract_param_defaults',
512+
'annotate_params',
513+
'param_pid_adjustment_update',
514+
'mavftp'
515+
)
516+
foreach ($script in $scripts) {
517+
Register-ArgumentCompleter -Native -CommandName $script -ScriptBlock {
518+
param($wordToComplete, $commandAst, $cursorPosition)
519+
$command = $script
520+
$env:COMP_LINE = $commandAst.ToString()
521+
$env:COMP_POINT = $cursorPosition
522+
$env:_ARGCOMPLETE = "1"
523+
$env:_ARGCOMPLETE_COMP_WORDBREAKS = " `"`'><=;|&(:"
524+
$env:COMP_WORDS = $commandAst.ToString()
525+
$env:COMP_CWORD = $cursorPosition
526+
527+
(& python -m argcomplete.completers $command) | ForEach-Object {
528+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
529+
}
530+
}
531+
}
532+
```

REUSE.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ path = "credits/ArduPilot_tempcal_IMU.py-COPYING.txt"
7777
SPDX-FileCopyrightText = "2024-2025 Amilcar Lucas"
7878
SPDX-License-Identifier = "GPL-3.0-or-later"
7979

80+
[[annotations]]
81+
path = "credits/argcomplete-LICENSE.rst"
82+
SPDX-FileCopyrightText = "2012-2025 Andrey Kislyuk"
83+
SPDX-License-Identifier = "Apache-2.0"
84+
8085
[[annotations]]
8186
path = "credits/charset-normalizer-LICENSE"
8287
SPDX-FileCopyrightText = "Copyright (c) 2019 TAHRI Ahmed R."

ardupilot_methodic_configurator/__main__.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#!/usr/bin/env python3
2+
# PYTHON_ARGCOMPLETE_OK
23

34
"""
45
The main application file. calls four sub-applications.
@@ -20,11 +21,13 @@
2021
from typing import Union
2122
from webbrowser import open as webbrowser_open
2223

24+
import argcomplete
25+
2326
from ardupilot_methodic_configurator import _, __version__
2427
from ardupilot_methodic_configurator.backend_filesystem import LocalFilesystem
2528
from ardupilot_methodic_configurator.backend_filesystem_program_settings import ProgramSettings
2629
from ardupilot_methodic_configurator.backend_flightcontroller import FlightController
27-
from ardupilot_methodic_configurator.common_arguments import add_common_arguments_and_parse
30+
from ardupilot_methodic_configurator.common_arguments import add_common_arguments
2831
from ardupilot_methodic_configurator.frontend_tkinter_base import show_error_message
2932
from ardupilot_methodic_configurator.frontend_tkinter_component_editor import ComponentEditorWindow
3033
from ardupilot_methodic_configurator.frontend_tkinter_connection_selection import ConnectionSelectionWindow
@@ -34,14 +37,12 @@
3437
from ardupilot_methodic_configurator.middleware_software_updates import UpdateManager, check_for_software_updates
3538

3639

37-
def argument_parser() -> argparse.Namespace:
40+
def create_argument_parser() -> argparse.ArgumentParser:
3841
"""
39-
Parses command-line arguments for the script.
40-
4142
This function sets up an argument parser to handle the command-line arguments for the script.
4243
4344
Returns:
44-
argparse.Namespace: An object containing the parsed arguments.
45+
argparse.ArgumentParser: The argument parser object.
4546
4647
"""
4748
parser = argparse.ArgumentParser(
@@ -63,7 +64,10 @@ def argument_parser() -> argparse.Namespace:
6364
parser = ComponentEditorWindow.add_argparse_arguments(parser)
6465
parser = ParameterEditorWindow.add_argparse_arguments(parser)
6566
parser = UpdateManager.add_argparse_arguments(parser)
66-
return add_common_arguments_and_parse(parser)
67+
parser = add_common_arguments(parser)
68+
69+
argcomplete.autocomplete(parser)
70+
return parser
6771

6872

6973
def connect_to_fc_and_set_vehicle_type(args: argparse.Namespace) -> tuple[FlightController, str]:
@@ -134,7 +138,7 @@ def component_editor(
134138

135139

136140
def main() -> None:
137-
args = argument_parser()
141+
args = create_argument_parser().parse_args()
138142

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

ardupilot_methodic_configurator/annotate_params.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#!/usr/bin/python3
2+
# PYTHON_ARGCOMPLETE_OK
23

34
"""
45
Fetches online ArduPilot parameter documentation (if not cached) locally.
@@ -36,6 +37,8 @@
3637
from typing import Any, Optional, Union
3738
from xml.etree import ElementTree as ET # no parsing, just data-structure manipulation
3839

40+
import argcomplete
41+
from argcomplete.completers import FilesCompleter
3942
from defusedxml import ElementTree as DET # noqa: N814, just parsing, no data-structure manipulation
4043

4144
# URL of the XML file
@@ -52,15 +55,15 @@
5255
# mypy: disable-error-code="unused-ignore"
5356

5457

55-
def arg_parser() -> argparse.Namespace:
58+
def create_argument_parser() -> argparse.ArgumentParser:
5659
parser = argparse.ArgumentParser(
5760
description="Fetches on-line ArduPilot parameter documentation and adds it to the "
5861
"specified file or to all *.param and *.parm files in the specified directory."
5962
)
6063
parser.add_argument(
6164
"target",
6265
help="The target file or directory.",
63-
)
66+
).completer = FilesCompleter(allowednames=(".param", ".parm"))
6467
parser.add_argument(
6568
"-d",
6669
"--delete-documentation-annotations",
@@ -107,6 +110,13 @@ def arg_parser() -> argparse.Namespace:
107110
help="Display version information and exit.",
108111
)
109112

113+
argcomplete.autocomplete(parser)
114+
return parser
115+
116+
117+
def parse_arguments() -> argparse.Namespace:
118+
parser = create_argument_parser()
119+
110120
args = parser.parse_args()
111121

112122
if args.verbose:
@@ -818,7 +828,7 @@ def parse_parameter_metadata(
818828

819829

820830
def main() -> None:
821-
args = arg_parser()
831+
args = parse_arguments()
822832
try:
823833
xml_url = get_xml_url(args.vehicle_type, args.firmware_version)
824834
xml_dir = get_xml_dir(args.target)

ardupilot_methodic_configurator/backend_filesystem.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
from typing import Any, Optional
2727
from zipfile import ZipFile
2828

29+
from argcomplete.completers import DirectoriesCompleter
30+
2931
from ardupilot_methodic_configurator import _
3032
from ardupilot_methodic_configurator.annotate_params import (
3133
PARAM_DEFINITION_XML_FILE,
@@ -677,7 +679,7 @@ def add_argparse_arguments(parser: ArgumentParser) -> ArgumentParser:
677679
help=_(
678680
"Directory containing vehicle-specific intermediate parameter files. Default is the current working directory"
679681
),
680-
)
682+
).completer = DirectoriesCompleter()
681683
parser.add_argument(
682684
"--n",
683685
type=int,

ardupilot_methodic_configurator/backend_flightcontroller.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,7 @@ def add_argparse_arguments(parser: ArgumentParser) -> ArgumentParser:
583583
'If set to "none" no connection is made.'
584584
" Default is autodetection"
585585
),
586-
)
586+
).completer = lambda **_: FlightController.__list_serial_ports()
587587
parser.add_argument(
588588
"-r",
589589
"--reboot-time",

0 commit comments

Comments
 (0)