Flux Python Development
These are notes for Python developers of Flux!
Development Environment
Note that we have a provided Dev Containers (VSCode) environment that you can
use to develop in. Instructions for using it are documented in the repository README. We recommend you use this environment (or the same container it
is built from in .devcontainer
for consistent results.
Linting
We have added support for basic automation of linting tasks, which you are
free to use or not. If you choose to not use these tools locally, they
will be checked in the continuous integration (and you can make changes
accordingly). Python 3.6 is required for the project, as a standard. You can
also continue to use the previous older custom linting and formatting
scripts in the scripts
folder of the repository.
Note
The flux
package which is used to interact with Flux cannot yet be installed into a virtual environment with pip or conda.
Install the dependencies for linting (note these will already be installed in the dev containers environment and you can skip this step):
$ pip install --force-reinstall -r scripts/requirements-dev.txt
This includes linting tools black, isort, mypy, and flake8, and pre-commit to automate running them. You can then (optionally) install the hook:
$ pre-commit install --hook-type pre-commit
You can optionally run pre-commit at any time like so:
$ pre-commit run --all-files
You'll see (on the first run) a bunch of errors, but likely the tools will fix them for you, and a new run will be much cleaner, with only a few manual fixes necessary:
Check for added large files..............................................Passed
Check for case conflicts.................................................Passed
Check docstring is first.................................................Passed
Check that scripts with shebangs are executable..........................Passed
Fix End of Files.........................................................Passed
Trim Trailing Whitespace.................................................Passed
Mixed line ending........................................................Passed
black....................................................................Passed
mypy.....................................................................Passed
And that's it! The check run in the CI is equivalent to here, so you should always be able to reproduce failures. And if you install the pre-commit hook, you can largely prevent them by always catching them before commit.
Writing Python Commands
The Flux command design is done in a way that some commands are generated from the C code, and others are helpers that come from Python. If you need to add a new command (and want it to show up) there are some tricks you need to know. This short guide will help you on this journey! 🏔️
Locations
Flux commands can be found under src/cmd
. You'll notice many files with
a prefix "flux-". By default, any file with this prefix will be discovered
and available to the user as a flux command. As an example, flux-run.py
will
be available as flux run
and flux-job.c
will be available as flux job
.
This shows that we have a mix of C derived and Python derived commands. if you are
interested in how this works, look at src/cmd/cmdhelp.c
.
Let's now say that we add a new command flux-foo.py
. We would expect to compile
Flux and see it available in flux help
, right? Wrong!
While the command will technically work as flux foo
, it won't show up in the help,
and there are a few tricks to getting that working, discussed next.
Adding a New Command
The command generation works as follows:
A new command should be a Python file added to
src/cmd
named likeflux-<command-name>.py
.The command can use (and write new) shared base classes that are found under
src/bindings/python/flux/cli
The script
etc/gen-cmdhelp.py
is run during build, and it looks for entries indoc/manpages.py
This means you need to add line entries for your new commands including the .rst file path relative to doc, e.g.:
('man1/flux-submit', 'flux-submit', 'submit a job to a Flux instance', [author], 1), ('man1/flux-run', 'flux-run', 'run a Flux job interactively', [author], 1), ('man1/flux-bulksubmit', 'flux-bulksubmit', 'submit jobs in bulk to a Flux instance', [author], 1), ('man1/flux-alloc', 'flux-alloc', 'allocate a new Flux instance for interactive use', [author], 1), ('man1/flux-batch', 'flux-batch', 'submit a batch script to Flux', [author], 1),
And of course, the dependency to that means actually writing the file! Python commands are in "man1."
Update
doc/Makefile.am
(MAN1_FILES_PRIMARY) andsrc/cmd/Makefile.am
(dist_fluxcmd_SCRIPTS) with your new file.Try to use shared logic whenever possible! E.g.,
doc/man1/common
has common snippets.The script generates
etc/flux/help.d/core.json
where you can sanity check the output.
Since these changes need to be compiled into the source code, be careful that if you re-generate that json
file, you do a make clean
to ensure that the cmd and etc directories are rebuilt. It's best to
start from scratch with make clean
as (in this writer's experience), partial cleans don't always work.
Updating a Command
Updating a command is much easier, as the documentation .rst file will already exist! This means that (depending on your updates) you should update this file, do a build from scratch, and check that:
The command functions as you would expect.
It shows up in help, and the help documentation is comprehensive and correct.
Tests for the command are updated or added.
Happy Developing!