AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified Unlicense Self-run

Running Python

skill-yale-som-hpc-claude-code-marketplace-running-python · by yale-som-hpc

Run Python on the Yale SOM HPC cluster with uv, Slurm, thread control, logging, and resumable outputs. TRIGGER when writing Python sbatch scripts for the Yale SOM HPC cluster, creating uv environments under /gpfs, or debugging Python Slurm jobs on the cluster.

No reviews yet
0 installs
15 views
0.0% view→install

Install

$ agentstack add skill-yale-som-hpc-claude-code-marketplace-running-python

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access Used
  • Shell / process execution No
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-yale-som-hpc-claude-code-marketplace-running-python)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
1mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Running Python? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Running Python

Rule: use a project environment, control threads, log clearly, and make outputs resumable.

What you get: with uv you control the interpreter and every package yourself, so nothing depends on what Python happens to be preinstalled on the cluster — no bootstrap step to worry about.

Tooling defaults

Use uv. It is the package manager for cluster Python work; everything below assumes it. Don't reach for conda, mamba, poetry, pipenv, pip-tools, or pip install --user — uv supersedes all of them. Why specifically uv on this cluster:

  • Single tool for dependencies, lockfile, virtualenv, and Python interpreter — no module/conda/pyenv stack to coordinate. The cluster's system python3 is old, and the python modules are a fixed set that changes between maintenance windows (check python3 --version and module spider python); uv downloads and pins whatever recent Python your project needs (pyproject.toml's requires-python).
  • Lockfile (uv.lock) is built-in and resolves identically on the login node, compute node, and your laptop — no conda env export games, no "works on my machine."
  • 10–100× faster than conda on GPFS. A uv sync --frozen is a few seconds; a conda env create is a multi-minute metadata storm because conda writes thousands of small files into one directory.
  • Single .venv directory in your project — easy to inspect, easy to nuke, easy to atomically swap.

These pair with uv:

  • ruff for lint + format. Catches mistakes locally before burning a Slurm allocation.
  • pyrefly for type checking. Same reason as ruff.
  • pytest for tests. Smoke tests on small inputs save many cluster reruns.
  • argparse for batch scripts (one-file entry points like run_task.py --task-id). click only when you grow into a reusable project CLI; the extra dependency is not worth it for a single sbatch script.
  • pathlib over os.path. Joining paths and checking parents is what you do most on the cluster.
  • logging as the baseline (configured below). loguru is fine when its structured output materially helps incident debugging.
  • pyproject.toml + uv.lock committed; .venv/ gitignored. The lockfile is what makes runs reproducible across login and compute nodes.

How to install the tools themselves: see [installing software](../installing-software/SKILL.md) for uv (one curl command into ~/.local/bin). Once uv is on your PATH, ruff, pyrefly, and pytest go in your project's dev dependencies via uv add --dev ruff pyrefly pytest, so they reproduce from uv.lock like everything else.

Project setup with uv

Pin a recent Python in pyproject.toml and let uv install it — don't depend on the cluster's system python3 (old enough that NumPy 2.x and many libraries are dropping support — check python3 --version) or a module load python/... (a fixed version that can change between maintenance windows; see module spider python):

cd /gpfs/project/myproject/code
uv init --app --python 3.13
uv add polars pyarrow duckdb
uv sync --frozen

uv python list shows what's available; uv python install 3.13 downloads it into ~/.local/share/uv/python/ (~50 MB per version) if uv hasn't already. The pinned version is recorded as requires-python in pyproject.toml, so anyone running uv sync on this project gets the same interpreter. Pick a current Python (uv python list shows options); drop one minor version if a critical dependency lags.

This is a setup-time operation, run once on a login node. Do not run uv sync inside Slurm jobs or job arrays — environment mutation in flight is a waste pattern (and --frozen makes it explicit that the lockfile is the source of truth).

Commit:

pyproject.toml
uv.lock

Do not commit .venv/; put it in .gitignore.

Don't pip install --user or run pip install inside jobs — neither is reproducible and both leak state between projects. For a one-off package, uv add then uv sync --frozen. See [installing software](../installing-software/SKILL.md) for the broader picture.

Data work, default picks

For most cluster work, these are the right defaults:

  • Tabular reads/writesPolars for new code (uses your CPU allocation through threading and lazy scan_*); pandas at API boundaries (sklearn, statsmodels, plotting). Convert with .to_pandas() only at the boundary; round-tripping doubles memory.
  • File format → Parquet with compression="zstd". One reused Parquet beats 10k CSVs both for speed and for GPFS metadata health.
  • Lazy readspl.scan_csv / pl.scan_parquet push filters and column projection before materialization, keeping memory under your --mem limit.
  • Append-heavy / streaming output → JSONL with gzip is the simplest correct option for record-by-record writes (one append-only file, atomic at line granularity). For columnar appends, write one Parquet per task or per chunk (out/task_0001.parquet, out/task_0002.parquet, …) and read them back with pl.scan_parquet("out/*.parquet"). Do not mutate one big Parquet in place. See [using the filesystem](../using-the-filesystem/SKILL.md) for the full append patterns.
  • SQL over local files → DuckDB. Joins CSV/Parquet/JSON without staging.
  • Per-project SQL state — catalogs, progress, lookups, OLTP-style writes → SQLite with WAL (PRAGMA journal_mode=WAL, synchronous=NORMAL, busy_timeout=15000). Handles concurrent writers (serialized via file locking) and non-blocking readers; fine on GPFS and on compute-node /local. The full pragma set + a batched ArtifactWriter pattern live in [acquiring data](../acquiring-data/SKILL.md#store-raw--metadata).
  • Multi-user databasepsycopg with psycopg_pool; create one pool per process if you fork.
  • Unknown encodingscharset-normalizer to detect, then pass encoding= explicitly.

Worked examples for query patterns and ingestion live in [accelerating Python](../accelerating-python/SKILL.md) and [acquiring data](../acquiring-data/SKILL.md).

Safe Python Slurm template

Use this shape as the default. The launch line is srun .venv/bin/python ..., not uv run python ..., so SIGUSR1 reaches Python on long jobs (see [parallel Python](../parallel-python/SKILL.md) for why):

#!/bin/bash
#SBATCH --job-name=python-job
# default_queue caps at 4h; for long/large work use cpunormal or gpunormal (see managing-jobs)
#SBATCH --partition=default_queue
#SBATCH --time=01:00:00
#SBATCH --cpus-per-task=4
#SBATCH --mem=16G
#SBATCH --output=logs/%x_%j.out

set -euo pipefail

export OMP_NUM_THREADS=${SLURM_CPUS_PER_TASK:-1}
export MKL_NUM_THREADS=${SLURM_CPUS_PER_TASK:-1}
export OPENBLAS_NUM_THREADS=${SLURM_CPUS_PER_TASK:-1}
export NUMEXPR_NUM_THREADS=${SLURM_CPUS_PER_TASK:-1}
export PYTHONUNBUFFERED=1                              # Slurm logs update during the job, not just at exit

cd /gpfs/project/myproject/code
# Environment was created during setup with: uv sync --frozen
srun .venv/bin/python src/main.py

For short, exploratory one-off jobs where signal-based shutdown does not matter, uv run python src/main.py (without srun) is acceptable. For anything long-running or resumable, use the srun .venv/bin/python form.

Read Slurm settings safely

SLURM_* env vars are only set inside Slurm jobs. The patterns below let the same script run on your laptop (no Slurm) and on the cluster (Slurm fills in real values):

import os

# Slurm allocation when running under sbatch/srun, fall back to the
# laptop's CPU count for local testing.
n_cpus = int(os.environ.get("SLURM_CPUS_PER_TASK", "0")) or os.cpu_count() or 1

# "local" is a useful sentinel for log file names off-cluster.
job_id = os.environ.get("SLURM_JOB_ID", "local")

The first line reads as "use Slurm's CPU count if it's set, else os.cpu_count(), else 1." On the cluster you get the allocation; on a laptop you get the local CPU count; in a constrained container you still get a sensible non-zero number.

Logging

import logging
import os

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s %(levelname)s %(message)s",
)

logging.info("job_id=%s", os.environ.get("SLURM_JOB_ID", "local"))

Set PYTHONUNBUFFERED=1 in the Slurm script (above) so log lines reach logs/*.out while the job is running, not all at once at the end. Use logs to know what happened without opening notebooks.

Multiprocessing

For nontrivial multiprocessing, producer-consumer queues, process pools, or job-stealing-style work, use [parallel Python](../parallel-python/SKILL.md).

Minimal rule: match worker count to allocated CPUs.

from concurrent.futures import ProcessPoolExecutor
import os

n_workers = int(os.environ.get("SLURM_CPUS_PER_TASK", "0")) or os.cpu_count() or 1

with ProcessPoolExecutor(max_workers=n_workers) as pool:
    results = list(pool.map(run_one_task, tasks))

Avoid nested parallelism: Slurm array × Python multiprocessing × BLAS threads can explode CPU usage.

Resumable numbered tasks

Write scripts so each Slurm array task can be restarted safely. If the output for task 17 exists, task 17 exits without doing work.

import argparse
from pathlib import Path

import polars as pl

parser = argparse.ArgumentParser()
parser.add_argument("--task-id", type=int, required=True)
args = parser.parse_args()

output = Path(f"/gpfs/project/myproject/output/task_{args.task_id:04d}.parquet")
if output.exists():
    print(f"task {args.task_id} already done: {output}", flush=True)
    raise SystemExit(0)

# Replace this with real task-specific work.
result = pl.DataFrame({"task_id": [args.task_id], "value": [args.task_id ** 2]})

tmp = output.with_suffix(".parquet.tmp")
result.write_parquet(tmp)
tmp.rename(output)

This pattern lets you rerun the same array and only compute missing tasks.

GPU check

Only use this in a GPU allocation:

import torch

assert torch.cuda.is_available(), "No CUDA device visible"
print(torch.cuda.get_device_name(0))

Checklist

  • [ ] Project uses uv (not conda / poetry / pipenv / pip+venv); pyproject.toml and uv.lock are committed.
  • [ ] pyproject.toml pins a recent Python via requires-python (e.g. >=3.13); the project does not depend on system python3 or module load python.
  • [ ] uv sync --frozen runs at setup, never inside arrays or jobs.
  • [ ] Slurm script sets OMP/MKL/OPENBLAS/NUMEXPR_NUM_THREADS and PYTHONUNBUFFERED=1.
  • [ ] Long or resumable jobs launch with srun .venv/bin/python ..., not uv run python ..., so SIGUSR1 reaches Python.
  • [ ] Python reads SLURM_CPUS_PER_TASK with a laptop-friendly fallback (os.cpu_count()).
  • [ ] Multiprocessing workers do not exceed allocated CPUs.
  • [ ] Outputs are skip-if-exists and atomically written.
  • [ ] Logs include job ID and key progress messages.

Further reading

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.