hpc_connect is a lightweight, configurable Python interface to HPC schedulers
and program launchers. It provides a single, scheduler-agnostic API for
submitting batch jobs and launching parallel applications, so the same code can
run against Slurm, PBS, Flux, a remote host, or the local machine simply by
changing configuration.
Support for each scheduler is provided by a backend plugin. The following backends ship with the package:
| Backend | Type string | Notes |
|---|---|---|
| Local | local |
Runs jobs directly on the current machine |
| Slurm | slurm |
sbatch/sacct submission, srun or MPI launch |
| PBS | pbs |
PBS Pro submission |
| Flux | flux |
Native-Python Flux backend |
| Flux (shell) | flux.sh |
Flux backend driven through the shell |
| Remote | remote_subprocess |
Dispatches to a remote host over a subprocess |
python3 -m pip install hpc-connectRequires Python 3.10 or newer.
hpc_connect is built around a few small pieces:
Backend— a scheduler abstraction selected from configuration. A backend knows the machine's resources (nodes, sockets, CPUs, GPUs) and hands out a submission manager and a launcher.JobSpec— a declarative, backend-agnostic description of a single submission: the commands to run, the resources requested, a time limit, environment, and I/O.- submission manager — translates a
JobSpecinto a real scheduler submission and returns aFuture. Future— a handle to a submitted job. It exposes the job id, return code, completion metadata, and start/jobid/done callbacks, and can be waited on individually or withas_completed.- launcher — builds and runs the parallel-launch command line
(
mpiexec,srun, …) for an application.
import hpc_connect
# Select by configured instance name, by backend type, or fall back to the
# single configured backend / the configured default.
backend = hpc_connect.get_backend("slurm")
print(backend.describe()) # human-readable resource summary
print(backend.node_count) # discovered node count
print(backend.count_per_node("cpu"))get_backend(name) resolves name against the configuration in this order:
- a backend entry whose
namematches, - otherwise a backend entry whose
typematches, - otherwise
nameis treated as a backend type with no per-entry overrides.
Called with no argument, it uses the configured default backend; if none is
set but exactly one backend is configured, that one is used; otherwise the call
is ambiguous and raises.
Use hpc_connect.backends() to list the registered backend types.
Describe the work with a JobSpec, submit it through the backend's submission
manager, and wait on the returned Future:
import hpc_connect
backend = hpc_connect.get_backend("slurm")
job = hpc_connect.JobSpec(
name="solve",
commands=["./solver --input deck.yaml"],
nodes=2,
cpus=64,
gpus=0,
time_limit=1800.0, # seconds
env={"OMP_NUM_THREADS": "4"},
output="solve.out",
error="solve.err",
)
future = backend.submission_manager().submit(job)
# Observe lifecycle events (each fires once, immediately if already reached).
future.add_jobid_callback(lambda f: print("queued as", f.jobid))
future.add_jobstart_callback(lambda f: print("running"))
future.add_done_callback(lambda f: print("done", f.returncode))
returncode = future.result() # blocks until the job finishes
info = future.proc_info() # scheduler/process metadataJobSpec is immutable; derive variants with job.with_updates(...) or
job.with_dependencies([...]). Backends that report supports_dependencies()
honor dependencies (e.g. Slurm afterany).
Use submission_manager().popen(job) to obtain the underlying process directly
instead of a Future, when you want to manage polling yourself.
from hpc_connect import as_completed
futures = [backend.submission_manager().submit(j) for j in jobs]
for future in as_completed(futures, timeout=3600):
print(future.jobid, future.returncode)as_completed yields futures as they finish and cancels any still pending if a
timeout or exception occurs.
A launcher builds the scheduler-appropriate parallel-launch command line and runs it:
backend = hpc_connect.get_backend()
launcher = backend.launcher()
result = launcher(["-n", "16", "./my_app", "--flag"], echo=True)
print(result.returncode)The command line is assembled roughly as:
<exec> <default_options> [user options] <pre_options> <application> [app options]
The launcher infers the process count from -n/-np (or the backend's
numproc_flag) and supports MPMD job specifications (segments separated by
:).
Backends expose helpers for planning allocations from a machine's discovered topology:
backend.node_count # total nodes
backend.count_per_node("gpu") # GPUs per node
backend.nodes_required(cpu=256) # nodes needed for 256 CPU tasks
backend.resource_view(ranks=128) # {np, ranks, nodes, sockets, ranks_per_socket}Two entry points are installed:
hpcc is the front-end wrapper. It exposes subcommands and applies
configuration overrides:
hpcc --info # show overview / configuration help
hpcc --version
hpcc -c backend:slurm launch -- ./my_app --flagConfiguration can be overridden inline with -c path:to:key:value.
hpc-launch is a convenience shim equivalent to hpcc launch. It launches an
application through the selected backend's launch configuration:
hpc-launch [launch options] <application> [application options]
hpc-launch --dryrun -n 16 ./my_app # print the command line without runningConfiguration is YAML under a top-level hpc_connect: key. The minimal case
just names a default backend:
hpc_connect:
backend: slurmThe full shape is:
hpc_connect:
debug: false
# Optional default backend for the CLI and get_backend() with no argument.
backend: this-site
backends:
- name: this-site # optional instance name (defaults to `type`)
type: slurm # required backend type (plugin id)
config: {} # backend-specific settings
# How applications are launched under this backend.
launch:
type: srun # e.g. "mpi" or "srun"
exec: srun # launch executable (backend may default this)
numproc_flag: -n # flag preceding the process count
default_options: [] # options placed before user arguments
pre_options: [] # options placed immediately before the application
variables: {} # environment overrides (name -> value)
mpmd:
global_options: []
local_options: []
# How jobs are submitted under this backend.
submit:
default_options: [] # options applied to every submission
polling_interval: 15.0Configuration is read and merged from three scopes (later scopes override earlier ones):
- Site —
sys.prefix/etc/hpc_connect/config.yaml(or$HPC_CONNECT_SITE_CONFIG) - Global —
~/.config/hpc_connect.yaml(or$HPC_CONNECT_GLOBAL_CONFIG, or$XDG_CONFIG_HOME/hpc_connect/config.yaml) - Local —
./hpc_connect.yaml
| Variable | Purpose |
|---|---|
HPC_CONNECT_DEBUG |
Enable debug logging (yes/true/1/on) |
HPC_CONNECT_LOG_LEVEL |
Set the log level explicitly (e.g. INFO, DEBUG) |
HPC_CONNECT_SITE_CONFIG |
Override the site configuration file path |
HPC_CONNECT_GLOBAL_CONFIG |
Override the global configuration file path |
HPC_CONNECT_HOSTFILE |
JSON file mapping host globs to resource specs (local backend) |
HPC_CONNECT_HOSTNAME |
Hostname used to match entries in HPC_CONNECT_HOSTFILE |
Local machine with an MPICH-style launcher:
hpc_connect:
backend: local
backends:
- type: local
launch:
type: mpi
exec: mpiexec
numproc_flag: -npSlurm with srun:
hpc_connect:
backend: slurm
backends:
- type: slurm
launch:
type: srun
exec: srunBackends are discovered through the hpc_connect entry-point group, so a new
scheduler can be added from a separate distribution without modifying
hpc_connect. A plugin registers a Backend subclass via the
hpc_connect_backend hook:
import hpc_connect
class MyBackend(hpc_connect.Backend):
type = "mybackend"
@classmethod
def default_config(cls) -> dict: ...
@property
def resource_specs(self) -> list[dict]: ...
@property
def valid_launchers(self) -> set[str]: ...
def submission_manager(self) -> hpc_connect.HPCSubmissionManager: ...
def launcher(self) -> hpc_connect.HPCLauncher: ...
@hpc_connect.hookimpl
def hpc_connect_backend():
return MyBackendExpose it in the plugin's pyproject.toml:
[project.entry-points."hpc_connect"]
mybackend = "my_package"The bundled backends (hpcc_slurm, hpcc_pbs, hpcc_flux, hpcc_remote) are
registered the same way and serve as reference implementations.
Before committing, run:
$ hpcc pre-commitIt runs the test suite and, if the tests pass, updates the version in
pyproject.toml.
Distributed under the MIT License. See COPYRIGHT and LICENSE for details.