Skip to content

Use subparser for argument parsing in simoc-sam.py#318

Draft
ezio-melotti wants to merge 2 commits into
masterfrom
refactor-argparsing
Draft

Use subparser for argument parsing in simoc-sam.py#318
ezio-melotti wants to merge 2 commits into
masterfrom
refactor-argparsing

Conversation

@ezio-melotti

Copy link
Copy Markdown
Collaborator

This PR attempts to refactor the argument parsing to use custom subparsers for each simoc-sam.py command. The goal is to allow passing named arguments -- a functionality needed by the @needs_root refactoring done in #317.

For example, if we have the following command:

@cmd
@needs_root
def test_cmd(posarg, kwarg1='default', kwarg2=None, kwargs3=None):
    pass

And we call it from another function as test_cmd(10, kwarg3='test'), @needs_root needs a way to pass both 10 and 'test' from the command line. Currently the arg parser only accepts positional args, so in order to pass kwargs3 we need to pass kwarg1 and kwarg2 positionally, but doing so (i.e. python3 simoc-sam.py test-cmd 10 default None test) would pass 'None' as a string.

By adding support for named arguments, @needs_root will be able to only pass kwargs3 (by calling python simoc-sam.py test-cmd 10 --kwarg3=test).

Since the implementation is rather tricky, I'm submitting this as a draft PR and will return to this later to review and complete the tests and polish the implementation -- for both the argparsing and the refactoring of @needs_root.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Refactors simoc-sam.py CLI argument parsing to use per-command subparsers generated from command function signatures, enabling --named=value arguments so decorators like @needs_root can re-invoke commands with kwargs.

Changes:

  • Update @needs_root to re-run only the decorated command via sudo ... simoc-sam.py <cmd> --key=value ....
  • Replace the flat cmd + *args parser with per-command subparsers and a dispatcher that maps parsed args back onto function parameters.
  • Add a new pytest suite covering parser behavior, main() dispatch behavior, and @needs_root command construction.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
simoc-sam.py Introduces subparser-based parsing (create_parser) and a new main() dispatcher; updates needs_root to emit named CLI args for subprocess re-invocation.
tests/test_simoc_sam_cli.py Adds tests for parsing, dispatch, and needs_root behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +48 to +55
def test_parser_positional_args():
"""Test parsing with positional arguments only."""
parser = simoc_sam_cli.create_parser()
args = parser.parse_args(['test-uid', 'wlan0', 'MyNetwork', 'password123'])

assert args.cmd == 'test-uid'
assert args._positional == ['wlan0', 'MyNetwork', 'password123']

Copilot AI Feb 18, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

create_parser() builds subparsers only for commands present in COMMANDS, but these tests call parse_args() with the subcommand test-uid, which is not defined anywhere in simoc-sam.py (so argparse will raise SystemExit before assertions run). Consider using clean_commands and registering a small dummy command (e.g., test_uid) for these parser-only tests, or switch the test inputs to an actual existing command name.

Copilot uses AI. Check for mistakes.
Comment thread simoc-sam.py
Comment on lines 589 to 593
def create_help(cmds):
help = ['Full list of available commands:']
for cmd, func in cmds.items():
help.append(f'{cmd.replace("_", "-"):18} {func.__doc__}')
return '\n'.join(help)

Copilot AI Feb 18, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

create_help() appears to be unused after switching to subparsers (create_parser() no longer calls it, and there are no other references). Consider removing it to avoid dead code, or re-integrate it into the top-level ArgumentParser help output if you still want a consolidated command list.

Copilot uses AI. Check for mistakes.
Comment thread simoc-sam.py
Comment on lines +635 to +662
for param_name, param in params:
if param.kind == inspect.Parameter.VAR_POSITIONAL:
# Collect all remaining positional args
call_args.extend(positional[pos_idx:])
break

# Check if provided via --flag (using SUPPRESS, so check hasattr)
if hasattr(args, param_name):
# Provided via --flag
named_value = getattr(args, param_name)
if param.default is inspect.Parameter.empty:
call_args.append(named_value)
else:
call_kwargs[param_name] = named_value
elif pos_idx < len(positional):
# Provided positionally
if param.default is inspect.Parameter.empty:
call_args.append(positional[pos_idx])
else:
call_kwargs[param_name] = positional[pos_idx]
pos_idx += 1
elif param.default is inspect.Parameter.empty:
# Required parameter not provided
parser.error(f'the following arguments are required: {param_name}')
# Otherwise: parameter has a default and wasn't provided, so don't pass it

cmd = args.cmd.replace('-', '_')
if cmd in COMMANDS:
result = COMMANDS[cmd](*args.args)
parser.exit(not result)
else:
cmds = ', '.join(cmd.replace('_', '-') for cmd in COMMANDS.keys())
parser.error(f'Command not found. Available commands: {cmds}')
result = func(*call_args, **call_kwargs)
sys.exit(0 if result else 1)

Copilot AI Feb 18, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

main() doesn’t validate that all values in args._positional were consumed. For commands with no parameters (or fewer parameters than provided), extra positional arguments are silently ignored (e.g., teardown-wifi extra would still run). Consider adding a check after the loop to parser.error(...) when pos_idx < len(positional) and the signature doesn’t include *args, to avoid masking user mistakes.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants