patman: Enhance implementation of file-based defaults

Patman allows defaults for its command-line flags to be read from a
file. The implementation of this does not fully work with subcommands,
since we don't want a default for those.

Also, it relies on being able to parse any sort of cmdline in order to
figure out the options that are available. But in some cases, the
cmdline may not parse, e.g. if there are required options, or
conflicting options.

Add a class which can be safely used to parse any cmdline, since it
allows execution to continue even when errors are obtained. Use this to
determine the defaults, being careful to skip sub/commands.

Another way to handle all of this would be to maintain the defaults
separately and insert them into the parser manually. For now, I'm not
sure which is best.

Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
Simon Glass
2025-05-10 13:05:07 +02:00
parent 42588591e2
commit 0ab5e441e7
2 changed files with 65 additions and 6 deletions

View File

@@ -21,6 +21,25 @@ PATMAN_DIR = pathlib.Path(__file__).parent
HAS_TESTS = os.path.exists(PATMAN_DIR / "func_test.py")
class ErrorCatchingArgumentParser(argparse.ArgumentParser):
def __init__(self, **kwargs):
self.exit_state = None
self.catch_error = False
super().__init__(**kwargs)
def error(self, message):
if self.catch_error:
self.message = message
else:
super().error(message)
def exit(self, status=0, message=None):
if self.catch_error:
self.exit_state = True
else:
super().exit(status, message)
def add_send_args(par):
"""Add arguments for the 'send' command
@@ -150,7 +169,7 @@ def setup_parser():
them as specified by tags you place in the commits. Use -n to do a dry
run first.'''
parser = argparse.ArgumentParser(epilog=epilog)
parser = ErrorCatchingArgumentParser(epilog=epilog)
parser.add_argument(
'-D', '--debug', action='store_true',
help='Enabling debugging (provides a full traceback on error)')

View File

@@ -9,8 +9,10 @@ except Exception:
import ConfigParser
import argparse
from io import StringIO
import os
import re
import sys
from u_boot_pylib import gitutil
@@ -254,10 +256,44 @@ def _UpdateDefaults(main_parser, config, argv):
defaults = {}
parser_defaults = []
argv = list(argv)
orig_argv = argv
bad = False
full_parser_list = []
for parser in parsers:
pdefs = parser.parse_known_args()[0]
parser_defaults.append(pdefs)
defaults.update(vars(pdefs))
argv_list = [orig_argv]
special_cases = []
if hasattr(parser, 'defaults_cmds'):
special_cases = parser.defaults_cmds
for action in parser._actions:
if action.choices:
argv_list = []
for choice in action.choices:
argv = None
for case in special_cases:
if case[0] == choice:
argv = case
argv_list.append(argv or [choice])
for argv in argv_list:
parser.message = None
old_val = parser.catch_error
try:
parser.catch_error = True
pdefs = parser.parse_known_args(argv)[0]
finally:
parser.catch_error = old_val
# if parser.message:
# print('bad', argv, parser.message)
# bad = True
parser_defaults.append(pdefs)
defaults.update(vars(pdefs))
full_parser_list.append(parser)
if bad:
print('Internal parsing error')
sys.exit(1)
# Go through the settings and collect defaults
for name, val in config.items('settings'):
@@ -272,11 +308,15 @@ def _UpdateDefaults(main_parser, config, argv):
defaults[name] = val
else:
print("WARNING: Unknown setting %s" % name)
if 'cmd' in defaults:
del defaults['cmd']
if 'subcmd' in defaults:
del defaults['subcmd']
# Set all the defaults and manually propagate them to subparsers
main_parser.set_defaults(**defaults)
assert len(parsers) == len(parser_defaults)
for parser, pdefs in zip(parsers, parser_defaults):
assert len(full_parser_list) == len(parser_defaults)
for parser, pdefs in zip(full_parser_list, parser_defaults):
parser.set_defaults(**{k: v for k, v in defaults.items()
if k in pdefs})
return defaults