Script Opinions
Scripts must be stateless, headless, reproducible, observable. This extends the coding-style skill for standalone Python scripts.
Scripts should be:
- General: reusable across datasets and parameters. No hardcoded paths, magic numbers, or assumptions.
- Atomic: Do one thing well — when scope grows, note it and ask whether to extend or split.
- Opinionated: Typed ingestion good; fallbacks and mode switches bad.
Non-negotiables
These exist to make scripts debuggable on a cluster you don't control — they are infrastructure, not style.
- Top-to-bottom execution. No hidden state, no
sys.path hacks. Anchor imports to __file__ or pip-install the repo.
- argparse for all config. No Hydra, no env-var configs. (If the repo already uses a framework, match it and note the deviation.)
- stdlib
logging only. logger.info/debug not print. (Same: match existing frameworks if present.)
- Headless. No
plt.show(), display(), input(). Use headless backends.
- Run directory per invocation with config snapshot,
logs/, artifacts/.
- Fail fast. Validate paths and assumptions before heavy work.
- Atomic writes. Write to
<path>.tmp, then os.replace(tmp, path). For checkpoints and anything preemption could corrupt.
- Strict imports. Loud
ImportError at top level unless truly optional.
- No env vars for config between script and submitter. Explicit args only.
Standard skeleton
The skeleton is a floor, not a ceiling — instrument, annotate, and structure the core logic however best serves the script. Use @dataclass by default; pydantic.BaseModel only for runtime validation.
@dataclass ExperimentConfig — typed, flat, hierarchical field names.
parse_args() -> ExperimentConfig — argparse. Pull defaults from dataclass.
setup_dir_save(config) -> str — creates dir_save/{logs,artifacts,checkpoints}.
setup_logging(dir_save, level) — dual handlers: FileHandler(logs/run.log) + StreamHandler(stdout). Both get the full log — no post-hoc copying from SLURM stdout.
set_determinism(seed, deterministic_torch=True), save_config(dir_save, config) (writes .yaml or .json).
run_experiment(config, logger) — the scientific meat. Readable, flat. The design here is yours — bring judgment about what to measure, log, and save.
main() wires: parse → dir → log → config → determinism → run.
Run directory layout
<dir_save>/
├── config.yaml # parsed args snapshot
├── logs/run.log # mirrored to stdout
├── artifacts/ # arrays, metrics, figures
└── checkpoints/ # optional; resumable state
CLI arg names
--dir_save, --name_run, --path_config, --path_data_*, --seed, --deterministic, --save-plots, --checkpoint-every-seconds, --resume.
Long-running jobs
- Preemption safety:
--checkpoint-every-{seconds,steps} + --resume. Atomic writes. Validate checkpoint before new work.
- Large I/O: try to implement lazy loading, streaming, or chunking.
- Multiprocess: each worker opens its own file handle. Never share HDF5/zarr handles across processes.
- Analysis plots: save underlying arrays alongside figures for local regeneration.
1---2name: script-opinions3description: Opinions for standalone Python scripts. Analysis runs, HPC jobs, batch processing.4---56# Script Opinions78Scripts must be **stateless, headless, reproducible, observable**. This extends the `coding-style` skill for standalone Python scripts.910Scripts should be:11- General: reusable across datasets and parameters. No hardcoded paths, magic numbers, or assumptions.12- Atomic: Do one thing well — when scope grows, note it and ask whether to extend or split. 13- Opinionated: Typed ingestion good; fallbacks and mode switches bad.1415## Non-negotiables1617These exist to make scripts debuggable on a cluster you don't control — they are infrastructure, not style.18191. **Top-to-bottom execution.** No hidden state, no `sys.path` hacks. Anchor imports to `__file__` or pip-install the repo.202. **argparse** for all config. No Hydra, no env-var configs. (If the repo already uses a framework, match it and note the deviation.)213. **stdlib `logging`** only. `logger.info`/`debug` not `print`. (Same: match existing frameworks if present.)224. **Headless.** No `plt.show()`, `display()`, `input()`. Use headless backends.235. **Run directory** per invocation with config snapshot, `logs/`, `artifacts/`.246. **Fail fast.** Validate paths and assumptions before heavy work.257. **Atomic writes.** Write to `<path>.tmp`, then `os.replace(tmp, path)`. For checkpoints and anything preemption could corrupt.268. **Strict imports.** Loud `ImportError` at top level unless truly optional.279. **No env vars** for config between script and submitter. Explicit args only.2829## Standard skeleton3031The skeleton is a floor, not a ceiling — instrument, annotate, and structure the core logic however best serves the script. Use `@dataclass` by default; `pydantic.BaseModel` only for runtime validation.3233- `@dataclass ExperimentConfig` — typed, flat, hierarchical field names.34- `parse_args() -> ExperimentConfig` — argparse. Pull defaults from dataclass.35- `setup_dir_save(config) -> str` — creates `dir_save/{logs,artifacts,checkpoints}`.36- `setup_logging(dir_save, level)` — dual handlers: `FileHandler(logs/run.log)` + `StreamHandler(stdout)`. Both get the full log — no post-hoc copying from SLURM stdout.37- `set_determinism(seed, deterministic_torch=True)`, `save_config(dir_save, config)` (writes `.yaml` or `.json`).38- `run_experiment(config, logger)` — the scientific meat. Readable, flat. The design here is yours — bring judgment about what to measure, log, and save.39- `main()` wires: parse → dir → log → config → determinism → run.4041## Run directory layout4243```text44<dir_save>/45├── config.yaml # parsed args snapshot46├── logs/run.log # mirrored to stdout47├── artifacts/ # arrays, metrics, figures48└── checkpoints/ # optional; resumable state49```5051## CLI arg names5253`--dir_save`, `--name_run`, `--path_config`, `--path_data_*`, `--seed`, `--deterministic`, `--save-plots`, `--checkpoint-every-seconds`, `--resume`.5455## Long-running jobs5657- **Preemption safety:** `--checkpoint-every-{seconds,steps}` + `--resume`. Atomic writes. Validate checkpoint before new work.58- **Large I/O:** try to implement lazy loading, streaming, or chunking.59- **Multiprocess:** each worker opens its own file handle. Never share HDF5/zarr handles across processes.60- **Analysis plots:** save underlying arrays alongside figures for local regeneration.