Skip to content

add CLIPlugin to accept arbitrary R shapes/JSON on the command-line #7252

Merged
mergify[bot] merged 7 commits into
flux-framework:masterfrom
wihobbs:cli-jobspec-resources-parser
Apr 30, 2026
Merged

add CLIPlugin to accept arbitrary R shapes/JSON on the command-line #7252
mergify[bot] merged 7 commits into
flux-framework:masterfrom
wihobbs:cli-jobspec-resources-parser

Conversation

@wihobbs

@wihobbs wihobbs commented Dec 19, 2025

Copy link
Copy Markdown
Member

This PR takes the excellent work done by @sam-maloney for RFC#46 and extends it into a command-line plugin that users can opt in to by setting FLUX_CLI_PLUGINPATH. This was originally suggested by @grondo as a way for us to test/get user feedback on the ergonomics of specifying shape on the command line, and make progress toward incorporating that option into our standard command line.

The plugin is relatively straightforward and the testing just references examples from the corresponding RFC. I know this is coming up right before many 🌴s, so no rush on reviews, but I wanted to get it to a stopping point before my own holiday begins :).

Comment thread src/bindings/python/flux/shape/parser.py Fixed
Comment thread src/bindings/python/flux/cli_jobspec/parser.py Fixed
@wihobbs wihobbs requested a review from trws January 7, 2026 22:40
@wihobbs wihobbs force-pushed the cli-jobspec-resources-parser branch 2 times, most recently from 25f1bbd to bd2e57b Compare January 12, 2026 20:41
@wihobbs wihobbs requested a review from grondo January 12, 2026 20:42
@wihobbs wihobbs force-pushed the cli-jobspec-resources-parser branch from bd2e57b to a432a63 Compare January 15, 2026 20:23
@wihobbs

wihobbs commented Feb 4, 2026

Copy link
Copy Markdown
Member Author

@sam-maloney was kind enough to do a quick review on 1/8/26 and commented in Slack:

I made some minor modifications to the commit in my branch to hopefully address the Python linting errors and CodeQL problems, so you can incorporate that easily enough. (@wihobbs: ✅)

  1. As for testing, my thinking was that if it could handle all the examples in RFC14 then that was a reasonably good start for testing valid coverage. I appreciate that you've added some invalid test cases, and I'm sure edge cases will come up as they always do, but I don't have much specific to suggest for now.
  2. And as for naming, I have no issue at all with removing "CLI". I just picked something, and the CLI was what I was thinking about at the time, but a short form certainly could be useful in other contexts, so a more general name would be great. Whether that is "shape" or "compressed_resources" or "rstring", etc. doesn't really matter to me, depending on whether it is preferred to have it short or verbose!

@grondo grondo 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.

Nice work! Just a few comments or questions to address inline. Thanks!

Comment thread src/bindings/python/flux/shape/plugin/shape.py Outdated
Comment thread t/t2718-python-cli-job-shape.t

@grondo grondo 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.

It is getting to the point where @sam-maloney's --resource-shape option will be really useful so I made another pass on this one.

I'm also wondering if we should install the plugin so it is picked up by default, instead of requiring users to modify their plugin path to get the feature.

Comment thread src/bindings/python/flux/Makefile.am
Comment thread src/bindings/python/flux/shape/plugin/shape.py Outdated
Comment thread src/bindings/python/flux/shape/plugin/shape.py Outdated
Comment thread src/bindings/python/flux/shape/plugin/shape.py Outdated
Comment thread src/bindings/python/flux/shape/plugin/shape.py Outdated
Comment thread t/python/t0041-job-shape-parser.py
Comment thread t/Makefile.am
Comment thread t/t2718-python-cli-job-shape.t Outdated
Comment thread t/t2718-python-cli-job-shape.t Outdated
Comment thread t/t2718-python-cli-job-shape.t
@garlick

garlick commented Mar 28, 2026

Copy link
Copy Markdown
Member

This would indeed be handy for toy scheduler experiments. Ping?

@wihobbs wihobbs closed this Mar 30, 2026
@wihobbs wihobbs force-pushed the cli-jobspec-resources-parser branch from 3d3172e to db89f76 Compare March 30, 2026 15:36
@wihobbs

wihobbs commented Mar 30, 2026

Copy link
Copy Markdown
Member Author

... I have no idea why this closed.

Edit: p.s. this is not ready for another review, a few more modifications needed, but will be soon.

@wihobbs wihobbs reopened this Mar 30, 2026
@wihobbs wihobbs force-pushed the cli-jobspec-resources-parser branch 4 times, most recently from 6e90be0 to 501c14e Compare March 30, 2026 19:27
@wihobbs

wihobbs commented Mar 30, 2026

Copy link
Copy Markdown
Member Author

This is ready for another review. I would like to get an ack from @sam-maloney before we merge, particularly on the testing and 501c14e, since it modifies his contribution and was Claude-generated. On balance, it looks reasonable to me, but Sam is the expert on the parser. If he's good with that, I will fold it up into the initial commit.

As for

I'm also wondering if we should install the plugin so it is picked up by default, instead of requiring users to modify their plugin path to get the feature.

that will depend on #7244, which I plan to go work on now.

@wihobbs wihobbs requested a review from grondo March 30, 2026 19:35
Comment thread src/bindings/python/flux/shape/plugin/shape.py Outdated
@wihobbs

wihobbs commented Mar 31, 2026

Copy link
Copy Markdown
Member Author

I was able to reproduce the el8 issue locally:

Generating LALR tables
Traceback (most recent call last):
  File "/usr/lib64/python3.6/runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "/usr/lib64/python3.6/runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "/usr/src/src/bindings/python/flux/shape/parser.py", line 666, in <module>
    print(yaml.dump(result, sort_keys=False))
  File "/usr/lib64/python3.6/site-packages/yaml/__init__.py", line 200, in dump
    return dump_all([data], stream, Dumper=Dumper, **kwds)
TypeError: dump_all() got an unexpected keyword argument 'sort_keys'
not ok 13 - flux.shape.parser --yaml produces yaml output
#
#		cat >expected10 <<-EOF &&
#		- type: slot
#		  count: 4
#		  label: task
#		  with:
#		  - type: node
#		    count: 1
#
#		EOF
#		$parser --yaml -- slot=4/node >output10 &&
#		test_cmp expected10 output10
#

According to the pyyaml changelog, support was added for the sort_keys option in v5.1. Unfortunately, the most recent rocky linux 8 package is 3.12.

I tried removing that key, but it does change the output:

--- expected10	2026-03-31 15:20:23.384955001 +0000
+++ output10	2026-03-31 15:20:23.489955001 +0000
@@ -1,7 +1,6 @@
-- type: slot
-  count: 4
+- count: 4
   label: task
+  type: slot
   with:
-  - type: node
-    count: 1
+  - {count: 1, type: node}

Perhaps that's not so bad, and would most likely be consistent across all distros? We could always add the key back in one glorious day when we finally no longer are tied to Python 3.6.8...

@wihobbs

wihobbs commented Mar 31, 2026

Copy link
Copy Markdown
Member Author

We could vendor pyyaml-5.1, or build it ourselves with pip. It appears to support python 3.6 according to the same changelog. However, if we do something to the el8 container, we'll have to remember and find a similar solution for all the TOSS 4 systems.

@grondo

grondo commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

I don't think we'd want to vendor a python package just for testing purposes.

Perhaps there is a workaround for sorting the output without sort_keys. A search online suggests using collections.OrderedDict before dumping to get sorted output.

@wihobbs

wihobbs commented Apr 8, 2026

Copy link
Copy Markdown
Member Author

@sam-maloney, barring the one outstanding test failure, would you mind taking a whirl through this and giving your thoughts? :)

@wihobbs wihobbs force-pushed the cli-jobspec-resources-parser branch 2 times, most recently from abc190d to c4d884a Compare April 24, 2026 19:14
@wihobbs

wihobbs commented Apr 24, 2026

Copy link
Copy Markdown
Member Author

@grondo, do you want to take another look at this for a secondary approval/change request?

Comment thread t/t2718-python-cli-job-shape.t Outdated

. $(dirname $0)/sharness.sh

test_under_flux 4 job

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.

Do these tests require a 4 node instance. If they do require a Flux instance, could you get away with 1 rank?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I like keeping the first test around, because I think it's always good to test that there isn't some logic in the plugin that fails if you don't use it, but you're right -- no need for a 4 node instance. Will swap to 1.

@grondo

grondo commented Apr 24, 2026

Copy link
Copy Markdown
Contributor

I noticed one quick thing. Will circle back again later to check this out again.

@wihobbs wihobbs force-pushed the cli-jobspec-resources-parser branch from c4d884a to 7987026 Compare April 27, 2026 16:54
@garlick

garlick commented Apr 30, 2026

Copy link
Copy Markdown
Member

@wihobbs - I know you've been tasked with some testing due early next week, but at a lower priority than that, it would be great to get this PR finalized for our next tag.

@wihobbs

wihobbs commented Apr 30, 2026

Copy link
Copy Markdown
Member Author

This is awaiting a review from @grondo who I believe is also juggling some high-priority items, but we agreed the same in our 1-1 this week. I expect we'll make the May release cut off. ;)

@grondo

grondo commented Apr 30, 2026

Copy link
Copy Markdown
Contributor

Was just taking a look at this. @wihobbs I guess you get the honor of fixing one last merge conflict in t/Makefile.am

@grondo grondo 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.

LGTM! A couple minor things inline.

Going forward, I wonder if we should remove the default_plugins list and instead just load all plugins found in the flux.cli.plugin namespace, since that's what is done with other plugin loading schemes in Flux and (I think?) with FLUX_CLI_PLUGINPATH.

Comment thread src/bindings/python/flux/cli/plugins/shape.py Outdated
Comment thread src/bindings/python/flux/cli/plugins/shape.py Outdated
@wihobbs wihobbs force-pushed the cli-jobspec-resources-parser branch 2 times, most recently from 4c04e93 to 91892d2 Compare April 30, 2026 22:05
@wihobbs

wihobbs commented Apr 30, 2026

Copy link
Copy Markdown
Member Author

Thanks @grondo! Setting MWP.

wihobbs and others added 7 commits April 30, 2026 15:40
Problem: the --list-plugins test asserts that only 5 plugins should
be listed with that option. Until recently that was true, but as plugins
get added to the `flux.cli.plugins` namespace this number will go up.
In general, it seems cumbersome to edit that test each time a new
plugin is added to that namespace.

Amend the logic to check for just one known plugin in the output.
Problem: there is a test in python/t0019-cli-plugin.py that checks
that if two plugins that have conflicting options are loaded, the
first one in the FLUX_CLI_PLUGINPATH* wins. Or so it seemed! All this
test was really doing was checking that the registry didn't load both
plugins, and never actually checked that the right plugin was loaded.

Edit the test to fix this oversight.
Problem: There is no parser for the CLI Jobspec Resource String Syntax
proposed in RFC 46.

Add a flux.shape.parser Python module which implements a parser
using the 'ply' module (mimicking the constraint parser).

For testing, the parser may be run from the command line with

 flux python -m flux.shape.parser [OPTIONS] -- EXPRESSION
Problem: currently, users have no way on the CLI of utilizing the
experimental parser in RFC 46 for specifying a job shape or a resource
set they'd like to use via JSON. This causes users to defer to
cumbersome piping and JSON mangling when they want to submit jobs of
arbitrary resource shapes on the command line.

Add long options via plugin `--resources-json` & `--resources-shape`
for specifying resources on the command line beyond the standard ones.
Provide this plugin by default; package it with flux-core into
`flux.cli.plugins` and add it to the `default_plugins` in
src/bindings/python/flux/cli/plugin.py.
Problem: there are examples of valid job shapes listed in RFC 46
but there is currently no unit test for the python jobspec shape
parser.

Add a unit test with the examples in RFC 46.
Problem: the jobspec shape/JSON plugin is currently not tested on
the command line.

Add a sharness test.
Problem: the RFC 46 resource shape parser is tested via its
corresponding CLIPlugin and with Python unit tests, but not as a
standalone tool.

Add t0039-shape-parser.t to fill the testing gap.

Assisted-By: Claude:claude-sonnet-4.5
@wihobbs wihobbs force-pushed the cli-jobspec-resources-parser branch from 91892d2 to 3c823cc Compare April 30, 2026 22:41
@wihobbs

wihobbs commented Apr 30, 2026

Copy link
Copy Markdown
Member Author

I guess you get the honor of fixing one last merge conflict in t/Makefile.am

Spoke too soon. There was yet another conflict. In the same file, on the same line.

@mergify mergify Bot added the queued label Apr 30, 2026
@mergify

mergify Bot commented Apr 30, 2026

Copy link
Copy Markdown
Contributor

Merge Queue Status

  • Entered queue2026-04-30 23:25 UTC · Rule: default
  • Checks skipped · PR is already up-to-date
  • Merged2026-04-30 23:25 UTC · at 3c823cccc428d18b68c319fa16b6ad8a1d50bdf1 · merge

This pull request spent 15 seconds in the queue, including 2 seconds running CI.

Required conditions to merge
  • #approved-reviews-by >= 1 [🛡 GitHub branch protection]
  • #changes-requested-reviews-by = 0 [🛡 GitHub branch protection]
  • any of [🛡 GitHub branch protection]:
    • check-success = validate commits
    • check-neutral = validate commits
    • check-skipped = validate commits
  • any of [🛡 GitHub branch protection]:
    • check-success = address-sanitizer check
    • check-neutral = address-sanitizer check
    • check-skipped = address-sanitizer check
  • any of [🛡 GitHub branch protection]:
    • check-success = coverage
    • check-neutral = coverage
    • check-skipped = coverage
  • any of [🛡 GitHub branch protection]:
    • check-success = focal - py3.8
    • check-neutral = focal - py3.8
    • check-skipped = focal - py3.8
  • any of [🛡 GitHub branch protection]:
    • check-success = docs/readthedocs.org:flux-core
    • check-neutral = docs/readthedocs.org:flux-core
    • check-skipped = docs/readthedocs.org:flux-core
  • any of [🛡 GitHub branch protection]:
    • check-success = inception
    • check-neutral = inception
    • check-skipped = inception
  • any of [🛡 GitHub branch protection]:
    • check-success = flux-sched check
    • check-neutral = flux-sched check
    • check-skipped = flux-sched check
  • any of [🛡 GitHub branch protection]:
    • check-success = el8 - system,coverage
    • check-neutral = el8 - system,coverage
    • check-skipped = el8 - system,coverage
  • any of [🛡 GitHub branch protection]:
    • check-success = spelling
    • check-neutral = spelling
    • check-skipped = spelling
  • any of [🛡 GitHub branch protection]:
    • check-success = el8 - ascii
    • check-neutral = el8 - ascii
    • check-skipped = el8 - ascii
  • any of [🛡 GitHub branch protection]:
    • check-success = bookworm - 32 bit
    • check-neutral = bookworm - 32 bit
    • check-skipped = bookworm - 32 bit
  • any of [🛡 GitHub branch protection]:
    • check-success = flux-accounting check
    • check-neutral = flux-accounting check
    • check-skipped = flux-accounting check
  • any of [🛡 GitHub branch protection]:
    • check-success = python linting
    • check-neutral = python linting
    • check-skipped = python linting
  • any of [🛡 GitHub branch protection]:
    • check-success = el9 - test-install
    • check-neutral = el9 - test-install
    • check-skipped = el9 - test-install
  • any of [🛡 GitHub branch protection]:
    • check-success = fedora40 - clang-18
    • check-neutral = fedora40 - clang-18
    • check-skipped = fedora40 - clang-18
  • any of [🛡 GitHub branch protection]:
    • check-success = noble - test-install
    • check-neutral = noble - test-install
    • check-skipped = noble - test-install
  • any of [🛡 GitHub branch protection]:
    • check-success = alpine - test-install
    • check-neutral = alpine - test-install
    • check-skipped = alpine - test-install
  • any of [🛡 GitHub branch protection]:
    • check-success = fedora40 - test-install
    • check-neutral = fedora40 - test-install
    • check-skipped = fedora40 - test-install
  • any of [🛡 GitHub branch protection]:
    • check-success = bookworm - test-install
    • check-neutral = bookworm - test-install
    • check-skipped = bookworm - test-install
  • any of [🛡 GitHub branch protection]:
    • check-success = jammy - test-install
    • check-neutral = jammy - test-install
    • check-skipped = jammy - test-install
  • any of [🛡 GitHub branch protection]:
    • check-success = flux-pam check
    • check-neutral = flux-pam check
    • check-skipped = flux-pam check
  • any of [🛡 GitHub branch protection]:
    • check-success = flux-pmix check
    • check-neutral = flux-pmix check
    • check-skipped = flux-pmix check
  • any of [🛡 GitHub branch protection]:
    • check-success = bookworm - gcc-12,distcheck
    • check-neutral = bookworm - gcc-12,distcheck
    • check-skipped = bookworm - gcc-12,distcheck
  • any of [🛡 GitHub branch protection]:
    • check-success = el10 - test-install
    • check-neutral = el10 - test-install
    • check-skipped = el10 - test-install

@mergify mergify Bot merged commit ba86ac2 into flux-framework:master Apr 30, 2026
34 of 35 checks passed
@mergify mergify Bot removed the queued label Apr 30, 2026
@codecov

codecov Bot commented Apr 30, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.74214% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.96%. Comparing base (fd9b718) to head (3c823cc).
⚠️ Report is 9 commits behind head on master.

Files with missing lines Patch % Lines
src/bindings/python/flux/cli/plugins/shape.py 92.85% 2 Missing ⚠️
src/bindings/python/flux/shape/parser.py 99.30% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #7252      +/-   ##
==========================================
+ Coverage   83.91%   83.96%   +0.04%     
==========================================
  Files         573      575       +2     
  Lines       96453    96770     +317     
==========================================
+ Hits        80943    81254     +311     
- Misses      15510    15516       +6     
Files with missing lines Coverage Δ
src/bindings/python/flux/cli/plugin.py 99.10% <100.00%> (+1.78%) ⬆️
src/bindings/python/flux/cli/plugins/shape.py 92.85% <92.85%> (ø)
src/bindings/python/flux/shape/parser.py 99.30% <99.30%> (ø)

... and 14 files with indirect coverage changes

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

@garlick

garlick commented May 1, 2026

Copy link
Copy Markdown
Member

Yay!

@wihobbs wihobbs deleted the cli-jobspec-resources-parser branch May 1, 2026 16:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants