From c0227bb3de526b92acbceb5e2504c8de11a864ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 26 Aug 2022 18:12:02 +0200 Subject: [PATCH] Add command line completion with argcomplete issue11592 review419481003 --- CHANGELOG | 1 + bin/trytond | 8 ++++++++ bin/trytond-admin | 8 ++++++++ bin/trytond-console | 8 ++++++++ bin/trytond-cron | 8 ++++++++ bin/trytond-stat | 8 ++++++++ bin/trytond-worker | 8 ++++++++ setup.py | 1 + trytond/commandline.py | 38 ++++++++++++++++++++++++++++++++------ 9 files changed, 82 insertions(+), 6 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index a31a88aac3..698c98b578 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Add command line completion with argcomplete * Include record name and value in validation error message * Add header parameter on export data * Enforce certificate validation for SMTP connection (issue11564) diff --git a/bin/trytond b/bin/trytond index 0eda1f580a..ead75aa34f 100755 --- a/bin/trytond +++ b/bin/trytond @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# PYTHON_ARGCOMPLETE_OK # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. import glob @@ -7,6 +8,11 @@ import os import sys import threading +try: + import argcomplete +except ImportError: + argcomplete = None + DIR = os.path.abspath(os.path.normpath(os.path.join(__file__, '..', '..', 'trytond'))) if os.path.isdir(DIR): @@ -16,6 +22,8 @@ import trytond.commandline as commandline from trytond.config import config, split_netloc parser = commandline.get_parser_daemon() +if argcomplete: + argcomplete.autocomplete(parser) options = parser.parse_args() commandline.config_log(options) extra_files = config.update_etc(options.configfile) diff --git a/bin/trytond-admin b/bin/trytond-admin index b2f6521573..d15e80b078 100755 --- a/bin/trytond-admin +++ b/bin/trytond-admin @@ -1,9 +1,15 @@ #!/usr/bin/env python3 +# PYTHON_ARGCOMPLETE_OK # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. import os import sys +try: + import argcomplete +except ImportError: + argcomplete = None + DIR = os.path.abspath(os.path.normpath(os.path.join(__file__, '..', '..', 'trytond'))) if os.path.isdir(DIR): @@ -13,6 +19,8 @@ import trytond.commandline as commandline from trytond.config import config parser = commandline.get_parser_admin() +if argcomplete: + argcomplete.autocomplete(parser) options = parser.parse_args() config.update_etc(options.configfile) commandline.config_log(options) diff --git a/bin/trytond-console b/bin/trytond-console index 44cb72bdc4..6ec5f00c84 100755 --- a/bin/trytond-console +++ b/bin/trytond-console @@ -1,9 +1,15 @@ #!/usr/bin/env python3 +# PYTHON_ARGCOMPLETE_OK # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. import os import sys +try: + import argcomplete +except ImportError: + argcomplete = None + DIR = os.path.abspath(os.path.normpath(os.path.join(__file__, '..', '..', 'trytond'))) if os.path.isdir(DIR): @@ -13,6 +19,8 @@ import trytond.commandline as commandline from trytond.config import config parser = commandline.get_parser_console() +if argcomplete: + argcomplete.autocomplete(parser) options = parser.parse_args() config.update_etc(options.configfile) diff --git a/bin/trytond-cron b/bin/trytond-cron index 8b9b8aa8ef..b2141e7beb 100755 --- a/bin/trytond-cron +++ b/bin/trytond-cron @@ -1,9 +1,15 @@ #!/usr/bin/env python3 +# PYTHON_ARGCOMPLETE_OK # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. import os import sys +try: + import argcomplete +except ImportError: + argcomplete = None + DIR = os.path.abspath(os.path.normpath(os.path.join(__file__, '..', '..', 'trytond'))) if os.path.isdir(DIR): @@ -13,6 +19,8 @@ import trytond.commandline as commandline from trytond.config import config parser = commandline.get_parser_cron() +if argcomplete: + argcomplete.autocomplete(parser) options = parser.parse_args() config.update_etc(options.configfile) commandline.config_log(options) diff --git a/bin/trytond-stat b/bin/trytond-stat index 1b7f483124..528c296cee 100755 --- a/bin/trytond-stat +++ b/bin/trytond-stat @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# PYTHON_ARGCOMPLETE_OK # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. import curses @@ -8,6 +9,11 @@ import os import sys from collections import defaultdict +try: + import argcomplete +except ImportError: + argcomplete = None + DIR = os.path.abspath(os.path.normpath(os.path.join(__file__, '..', '..', 'trytond'))) if os.path.isdir(DIR): @@ -17,6 +23,8 @@ import trytond.commandline as commandline from trytond.config import config parser = commandline.get_parser_stat() +if argcomplete: + argcomplete.autocomplete(parser) options = parser.parse_args() config.update_etc(options.configfile) diff --git a/bin/trytond-worker b/bin/trytond-worker index 0e9024bb7c..f8de953109 100755 --- a/bin/trytond-worker +++ b/bin/trytond-worker @@ -1,9 +1,15 @@ #!/usr/bin/env python3 +# PYTHON_ARGCOMPLETE_OK # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. import os import sys +try: + import argcomplete +except ImportError: + argcomplete = None + DIR = os.path.abspath(os.path.normpath(os.path.join(__file__, '..', '..', 'trytond'))) if os.path.isdir(DIR): @@ -13,6 +19,8 @@ import trytond.commandline as commandline from trytond.config import config parser = commandline.get_parser_worker() +if argcomplete: + argcomplete.autocomplete(parser) options = parser.parse_args() config.update_etc(options.configfile) commandline.config_log(options) diff --git a/setup.py b/setup.py index dddd38c94d..f85aba5e85 100755 --- a/setup.py +++ b/setup.py @@ -173,6 +173,7 @@ setup(name=name, 'weasyprint': ['weasyprint'], 'coroutine': ['gevent>=1.1'], 'image': ['pillow'], + 'completion': ['argcomplete'], }, dependency_links=dependency_links, zip_safe=False, diff --git a/trytond/commandline.py b/trytond/commandline.py index 3ad2b87e38..3cfc8823c7 100644 --- a/trytond/commandline.py +++ b/trytond/commandline.py @@ -6,6 +6,7 @@ import logging import logging.config import logging.handlers import os +import os.path from contextlib import contextmanager from io import StringIO @@ -14,6 +15,25 @@ from trytond import __version__ logger = logging.getLogger(__name__) +def database_completer(parsed_args, **kwargs): + from trytond.config import config + from trytond.transaction import Transaction + config.update_etc(parsed_args.configfile) + with Transaction().start( + None, 0, readonly=True, close=True) as transaction: + return transaction.database.list() + + +def module_completer(**kwargs): + from trytond.modules import get_module_list + return get_module_list() + + +def language_completer(**kwargs): + files = os.listdir(os.path.join(os.path.dirname(__file__), 'ir', 'locale')) + return [os.path.splitext(f)[0] for f in files] + + def get_base_parser(): parser = argparse.ArgumentParser() parser.add_argument('--version', action='version', @@ -37,8 +57,10 @@ def get_parser(): db_names = list(next(csv.reader(StringIO(db_names)))) else: db_names = [] - parser.add_argument("-d", "--database", dest="database_names", nargs='+', - default=db_names, metavar='DATABASE', help="specify the database name") + parser.add_argument( + "-d", "--database", dest="database_names", nargs='+', + default=db_names, metavar='DATABASE', + help="specify the database name").completer = database_completer parser.add_argument("--logconf", dest="logconf", metavar='FILE', help="logging configuration file (ConfigParser format)") @@ -77,8 +99,10 @@ def get_parser_cron(): def get_parser_admin(): parser = get_parser() - parser.add_argument("-u", "--update", dest="update", nargs='+', default=[], - metavar='MODULE', help="activate or update a module") + parser.add_argument( + "-u", "--update", dest="update", nargs='+', default=[], + metavar='MODULE', + help="activate or update a module").completer = module_completer parser.add_argument("--all", dest="update", action="append_const", const="ir", help="update all activated modules") parser.add_argument("--activate-dependencies", dest="activatedeps", @@ -93,8 +117,10 @@ def get_parser_admin(): help="Send a test email to the specified address.") parser.add_argument("-m", "--update-modules-list", action="store_true", dest="update_modules_list", help="Update list of tryton modules") - parser.add_argument("-l", "--language", dest="languages", nargs='+', - default=[], metavar='CODE', help="Load language translations") + parser.add_argument( + "-l", "--language", dest="languages", nargs='+', + default=[], metavar='CODE', + help="Load language translations").completer = language_completer parser.add_argument("--hostname", dest="hostname", default=None, help="Limit database listing to the hostname") parser.add_argument("--validate", dest="validate", nargs='*',