Tests may want to parse their own arguments. Refactor the parser code to support this and allow settings to receive arguments as well.
Signed-off-by: Simon Glass <s...@chromium.org> --- tools/patman/cmdline.py | 32 +++++++++++++++++++++++++------- tools/patman/settings.py | 28 ++++++++++++++++++---------- tools/patman/test_settings.py | 2 +- 3 files changed, 44 insertions(+), 18 deletions(-) diff --git a/tools/patman/cmdline.py b/tools/patman/cmdline.py index cf32716b8e0..e5ac4fb1684 100644 --- a/tools/patman/cmdline.py +++ b/tools/patman/cmdline.py @@ -20,13 +20,11 @@ from patman import settings PATMAN_DIR = pathlib.Path(__file__).parent HAS_TESTS = os.path.exists(PATMAN_DIR / "func_test.py") -def parse_args(): - """Parse command line arguments from sys.argv[] +def setup_parser(): + """Set up command-line parser Returns: - tuple containing: - options: command line options - args: command lin arguments + argparse.Parser object """ epilog = '''Create patches from commits in a branch, check them and email them as specified by tags you place in the commits. Use -n to do a dry @@ -132,14 +130,34 @@ def parse_args(): help='Force overwriting an existing branch') status.add_argument('-T', '--single-thread', action='store_true', help='Disable multithreading when reading patchwork') + return parser + + +def parse_args(argv=None, config_fname=None, parser=None): + """Parse command line arguments from sys.argv[] + + Args: + argv (str or None): Arguments to process, or None to use sys.argv[1:] + config_fname (str): Config file to read, or None for default, or False + for an empty config + + Returns: + tuple containing: + options: command line options + args: command lin arguments + """ + if not parser: + parser = setup_parser() # Parse options twice: first to get the project and second to handle # defaults properly (which depends on project) # Use parse_known_args() in case 'cmd' is omitted - argv = sys.argv[1:] + if not argv: + argv = sys.argv[1:] + args, rest = parser.parse_known_args(argv) if hasattr(args, 'project'): - settings.Setup(parser, args.project) + settings.Setup(parser, args.project, argv, config_fname) args, rest = parser.parse_known_args(argv) # If we have a command, it is safe to parse all arguments diff --git a/tools/patman/settings.py b/tools/patman/settings.py index d66b22be1df..7a0866cd370 100644 --- a/tools/patman/settings.py +++ b/tools/patman/settings.py @@ -226,7 +226,7 @@ nxp = Zhikang Zhang <zhikang.zh...@nxp.com> f.close() -def _UpdateDefaults(main_parser, config): +def _UpdateDefaults(main_parser, config, argv): """Update the given OptionParser defaults based on config. We'll walk through all of the settings from all parsers. @@ -242,6 +242,7 @@ def _UpdateDefaults(main_parser, config): updated. config: An instance of _ProjectConfigParser that we will query for settings. + argv (list of str or None): Arguments to parse """ # Find all the parsers and subparsers parsers = [main_parser] @@ -252,6 +253,7 @@ def _UpdateDefaults(main_parser, config): # Collect the defaults from each parser defaults = {} parser_defaults = [] + argv = list(argv) for parser in parsers: pdefs = parser.parse_known_args()[0] parser_defaults.append(pdefs) @@ -273,9 +275,11 @@ def _UpdateDefaults(main_parser, config): # 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): parser.set_defaults(**{k: v for k, v in defaults.items() if k in pdefs}) + return defaults def _ReadAliasFile(fname): @@ -334,7 +338,7 @@ def GetItems(config, section): return [] -def Setup(parser, project_name, config_fname=None): +def Setup(parser, project_name, argv, config_fname=None): """Set up the settings module by reading config files. Unless `config_fname` is specified, a `.patman` config file local @@ -347,8 +351,9 @@ def Setup(parser, project_name, config_fname=None): parser: The parser to update. project_name: Name of project that we're working on; we'll look for sections named "project_section" as well. - config_fname: Config filename to read. An error is raised if it - does not exist. + config_fname: Config filename to read, or None for default, or False + for an empty config. An error is raised if it does not exist. + argv (list of str or None): Arguments to parse, or None for default """ # First read the git alias file if available _ReadAliasFile('doc/git-mailrc') @@ -357,12 +362,15 @@ def Setup(parser, project_name, config_fname=None): if config_fname and not os.path.exists(config_fname): raise Exception(f'provided {config_fname} does not exist') - if not config_fname: + if config_fname is None: config_fname = '%s/.patman' % os.getenv('HOME') - has_config = os.path.exists(config_fname) - git_local_config_fname = os.path.join(gitutil.get_top_level(), '.patman') - has_git_local_config = os.path.exists(git_local_config_fname) + + has_config = False + has_git_local_config = False + if config_fname is not False: + has_config = os.path.exists(config_fname) + has_git_local_config = os.path.exists(git_local_config_fname) # Read the git local config last, so that its values override # those of the global config, if any. @@ -371,7 +379,7 @@ def Setup(parser, project_name, config_fname=None): if has_git_local_config: config.read(git_local_config_fname) - if not (has_config or has_git_local_config): + if config_fname is not False and not (has_config or has_git_local_config): print("No config file found.\nCreating ~/.patman...\n") CreatePatmanConfigFile(config_fname) @@ -382,7 +390,7 @@ def Setup(parser, project_name, config_fname=None): for name, value in GetItems(config, 'bounces'): bounces.add(value) - _UpdateDefaults(parser, config) + return _UpdateDefaults(parser, config, argv) # These are the aliases we understand, indexed by alias. Each member is a list. diff --git a/tools/patman/test_settings.py b/tools/patman/test_settings.py index 06b7cbc3ab6..c117836de31 100644 --- a/tools/patman/test_settings.py +++ b/tools/patman/test_settings.py @@ -49,7 +49,7 @@ def test_git_local_config(): dest='check_patch', default=True) # Test "global" config is used. - settings.Setup(parser, 'unknown', global_config.name) + settings.Setup(parser, 'unknown', None, global_config.name) args, _ = parser.parse_known_args([]) assert args.project == 'u-boot' send_args, _ = send.parse_known_args([]) -- 2.43.0