Skip to content

python: improve CLI option parsing with FluxArgumentParser#7675

Open
grondo wants to merge 9 commits into
flux-framework:masterfrom
grondo:argparse-improve
Open

python: improve CLI option parsing with FluxArgumentParser#7675
grondo wants to merge 9 commits into
flux-framework:masterfrom
grondo:argparse-improve

Conversation

@grondo

@grondo grondo commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

This PR adds a FluxArgumentParser class and uses it throughout the Python CLI commands so they share consistent argument parsing behavior. The changes implemted in FluxArgumentParser vs Python argparse include:

  • Don't accept abbreviated options by default (default allow_abbrev=False) On Python 3.6/3.7, where allow_abbrev=False would also break short-option concatenation (-vvv, -n4) due to bpo-26967, the 3.8 fix is backported via a version-gated override instead.
  • A hidden_aliases extension to add_argument() preserves a few commonly-used former abbreviations (--dry, --state, --plugin, --require, --broker-opt) without advertising them in --help.
  • Submission commands (flux run, submit, alloc, batch, bulksubmit) now use POSIX-style option parsing: With posix=True, FluxArgumentParser option processing stops at the first non-option argument, so tokens after the job command are never consumed as Flux options regardless of ordering. A -- separator still works as before.
  • The Flux help formatter is now the default everywhere, removing repetitive formatter_class= boilerplate.

Fixes #7644

grondo and others added 9 commits June 12, 2026 15:00
Problem: Python flux commands use argparse.ArgumentParser with the
default allow_abbrev=True, which silently matches abbreviated option
strings. This can cause unintended behavior when tokens intended for
a job command are consumed as abbreviated Flux options.

Add FluxArgumentParser, a subclass of argparse.ArgumentParser that
sets allow_abbrev=False and defaults to the Flux help formatter. It
supports a hidden_aliases kwarg on add_argument() for option strings
that should be recognized but suppressed from --help output, allowing
migration from previously-working abbreviations without unnecessarily
breaking existing usage. The companion _FluxArgumentGroup class
ensures the same behavior applies when add_argument() is called on
an argument group. Update FluxHelpFormatter in util.py to filter
hidden_option_strings from the formatted option invocation string.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Problem: FluxArgumentParser and its hidden_aliases support have no
unit tests, making it difficult to verify correctness or catch
regressions.

Add t/python/t0047-flux-argument-parser.py covering allow_abbrev
enforcement, default help formatter behavior, subparser class
inheritance, and hidden_aliases operation via both direct parser
add_argument() calls and argument groups.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Problem: Python flux commands use argparse.ArgumentParser with
allow_abbrev=True, which silently matches abbreviated option strings.
This can cause unintended behavior when tokens intended for a job
command are consumed as abbreviated Flux options.

Replace all argparse.ArgumentParser usage in command and library
Python files with FluxArgumentParser from flux.cli.argparse, which
enforces allow_abbrev=False and applies the Flux help formatter by
default. Add hidden singular aliases (--state, --plugin, --require,
--broker-opt) for plural options where abbreviation previously worked
via allow_abbrev, to avoid unnecessarily breaking existing usage
during the transition. Fix a typo in t2272-job-begin-time.t where
--begin=time=+1s relied on --begin abbreviating --begin-time.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Problem: Submission commands (flux run, submit, alloc, batch) accept
a job command and its arguments as a trailing REMAINDER positional.
With GNU-style argparse, option-like tokens that appear after the job
command are still consumed as Flux options, making behavior depend on
argument ordering in unexpected ways.

Add a posix=True constructor parameter to FluxArgumentParser that
splits argv at the first non-option argument before parsing. Options
before the first positional are parsed normally; everything from the
first positional onward is injected into the REMAINDER dest. A
user-supplied '--' separator is preserved as the first element of the
positionals. If there is no REMAINDER dest and positionals remain,
the parser raises an "unrecognized arguments" error. Negative-number-
like tokens (e.g. -1) are treated as non-options when the parser has
no negative-number optionals, matching stock argparse behavior. Options
with nargs='+' or nargs='*' greedily consume consecutive non-option
tokens before the split point. Enable posix=True in
MiniCmd.create_parser() so all submission commands get this behavior.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Problem: The posix=True mode added to FluxArgumentParser has no unit
tests, making it difficult to verify correctness or catch regressions.

Add a TestPosixMode class to t/python/t0047-flux-argument-parser.py
covering option-before-command parsing, option-after-command capture,
user-supplied '--' preservation, short option value skipping, and the
guarantee that no implicit '--' appears in the REMAINDER result.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Problem: The new POSIX mode in FluxArgumentParser has no coverage in
the existing per-command sharness tests, making it easy to regress
without notice.

Add one test to each of t2710 (submit), t2711 (run), t2712 (alloc),
t2713 (bulksubmit), and t2714 (batch) verifying that a recognized Flux
option appearing after the job command is captured in the REMAINDER
positional rather than consumed by the Flux parser.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Problem: FluxArgumentParser sets allow_abbrev=False to reject
abbreviated long options, but on Python 3.6/3.7 that setting also
breaks short-option concatenation (-vvv, -n4) due to bpo-26967: those
versions gate the entire _get_option_tuples call in _parse_optional
on allow_abbrev. Simply skipping allow_abbrev=False on old Pythons
would leave EL8 users (3.6/3.7) with long-option abbreviations
enabled and behavior differing by platform.

Backport the bpo-26967 fix for Python 3.6/3.7 by overriding
_get_option_tuples at class body level under a sys.version_info < (3,
8) guard: for long options (starting with two prefix chars), return []
to suppress prefix matching; for short options fall through to super()
so concatenation works normally. On Python >= 3.8, set
allow_abbrev=False (the bpo-26967 fix moved the gate inside
_get_option_tuples in 3.8, so only long-option abbreviation is gated
there). 3.6/3.7 are frozen/EOL so the private-method override cannot
drift. The override was verified against the real CPython 3.6
argparse.py: -vvv, -vn4, and -n 4 all work; --foo abbreviations of
--foo-bar are rejected. With uniform behavior across all supported
Pythons, the abbrev tests in t0047 need no version skip.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Problem: Users have been relying on --dry as an abbreviation for
--dry-run in flux submit, run, alloc, batch, bulksubmit, cancel,
update, and modprobe load. With allow_abbrev=False this abbreviation
no longer works.

Add --dry as a hidden alias for --dry-run in MiniCmd.create_parser()
covering all submission commands, in flux-cancel, in flux-update, and
in the load subcommand of flux-modprobe, to avoid unnecessarily
breaking existing usage.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Problem: flux-run, flux-submit, flux-alloc, flux-batch, and
flux-bulksubmit now stop option processing at the first non-option
argument and no longer accept abbreviated long options, but the man
pages do not describe this behavior.

Add a prose note at the end of the OTHER OPTIONS section (via the
shared job-other-options.rst fragment included by all five submission
command pages) describing the new behavior: option processing stops at
the first non-option argument so COMMAND and its arguments are never
consumed as Flux options, and long options may no longer be
abbreviated. The note also clarifies that a ``--`` separator is still
accepted and that flux-alloc forwards it to the broker.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@codecov

codecov Bot commented Jun 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.70330% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 84.02%. Comparing base (5848ad7) to head (96f7532).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
src/bindings/python/flux/cli/argparse.py 95.69% 4 Missing ⚠️
...indings/python/flux/job/frobnicator/frobnicator.py 66.66% 1 Missing ⚠️
...rc/bindings/python/flux/job/validator/validator.py 66.66% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #7675      +/-   ##
==========================================
+ Coverage   83.99%   84.02%   +0.03%     
==========================================
  Files         577      578       +1     
  Lines       97511    97614     +103     
==========================================
+ Hits        81907    82024     +117     
+ Misses      15604    15590      -14     
Files with missing lines Coverage Δ
src/bindings/python/flux/cli/base.py 95.12% <100.00%> (+0.01%) ⬆️
src/bindings/python/flux/cli/fortune.py 84.61% <100.00%> (-0.30%) ⬇️
src/bindings/python/flux/sdexec/map.py 83.62% <100.00%> (-0.08%) ⬇️
src/bindings/python/flux/util.py 96.64% <100.00%> (+0.01%) ⬆️
src/cmd/flux-admin.py 92.16% <100.00%> (+0.03%) ⬆️
src/cmd/flux-cancel.py 95.40% <100.00%> (ø)
src/cmd/flux-hostlist.py 97.36% <100.00%> (ø)
src/cmd/flux-housekeeping.py 87.20% <100.00%> (ø)
src/cmd/flux-job-exec-override.py 94.11% <100.00%> (ø)
src/cmd/flux-job-frobnicator.py 93.44% <100.00%> (+0.10%) ⬆️
... and 18 more

... and 13 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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.

flux grabbing command line argument after executable

1 participant