Skip to main content

Stimela 3.0 -- a functional, flexible, and simple framework for reproducible radio astronomy pipelines

Project description

A spiritual successor to Stimela classic, built around the same core philosophy: functional and flexible simplicity for reproducible radio astronomy pipelines.

Recipes are plain Python. A step is a function call; a step’s output is a Python value you wire into the next call. There is no YAML expression/substitution language, no alias-propagation system, and no stacked config libraries – control flow is just Python, and it doesn’t need reinventing.

from pydantic import BaseModel

from shinobi import Cab, Recipe, step


class ImageInputs(BaseModel):
    ms: str = "obs.ms"
    prefix: str = "img"


class ImageOutputs(BaseModel):
    restored: str | None = None


wsclean = Cab(
    name="wsclean",
    command="wsclean",
    image="quay.io/stimela/wsclean:latest",
    inputs_model=ImageInputs,
    outputs_model=ImageOutputs,
)


@step(wsclean, backend="native")
def image(ctx):
    """Image the visibilities. A near-empty body auto-runs the cab."""
    return ctx.run()

Run it straight from the command line – the step’s schema becomes the CLI options, no entrypoint script required:

ninja run myrecipe.py:image --ms data.ms --prefix out

Architecture

  • Cabs (shinobi.Cab) – a typed, backend-agnostic description of an atomic task: an inputs/outputs schema (pydantic models) plus policies for turning parameters into a CLI invocation. Define one directly in Python, or load one from existing cult-cargo YAML (shinobi.loaders.cultcargo) – that schema format is good design and is reused as-is, including its _include (file composition) and _use (dotted-path deep-merge) mechanisms, verified against real upstream cab files. The =config.x.y expression language and package-scoped includes are deliberately not implemented – see the module docstring and AGENTS.md.

  • Steps (shinobi.step, shinobi.pystep) – a step binds an orchestration function to a scope. @shinobi.step decorates a function with an existing Cab/Recipe; its body receives an ExecContext (ctx) and calls ctx.run() to execute. @shinobi.pystep turns a plain, type-hinted Python function into a step, deriving its schema from the signature – no external tool, no hand-written models.

  • Backends (shinobi.backends) – pluggable executors, all shelling out to the relevant CLI rather than a Python SDK: native (subprocess), docker/podman/apptainer, slurm (sbatch/sacct), kubernetes (kubectl, batch Jobs). Every backend blocks until the job finishes and returns a Result – no async mode, recipes are plain Python. Container/cluster backends derive bind mounts from the cab’s own schema (File/MS-dtype params get their parent dir mounted). native/container backends were verified against a real quay.io/stimela/wsclean image; kubernetes against a real kind cluster; slurm was not live-verified (no cluster was available in the dev environment) – see AGENTS.md for what that means in practice.

  • Recipes (shinobi.Recipe) – just Python. A Recipe composes steps, wiring one step’s output into the next either declaratively (via StepRef/InputRef/OutputRef, or the recipe.inputs / recipe.outputs proxies and add_step) or through an orchestration function whose body is ordinary Python.

  • Config (shinobi.config.AppConfig) – layered settings via pydantic-settings: built-in defaults < config file < env vars (SHINOBI_*) < explicit overrides.

CLI

Every Cab, Recipe, or @shinobi.step-decorated function can be run directly, without writing a Python entrypoint script – its signature/schema becomes CLI options automatically:

ninja run myrecipe.py:image --ms data.ms --prefix out
ninja run myrecipe.py:selfcal --ms data.ms

ninja run <target> resolves <target> (path/to/file.py:name or a dotted module path) to the Cab, Recipe, or StepRef it names and dispatches it with the parsed options.

Add --dryrun to see the execution graph a recipe would produce, without running anything:

$ ninja run myrecipe.py:selfcal --ms data.ms --dryrun
[ image ]
    |
    v
[ mask ]

This actually runs the recipe’s real Python code (so its if/for do whatever they’d really do for the given options), just with every cab swapped for a no-op that records the call instead of executing it – so it only ever shows the one path taken for these inputs, never an untaken branch. Fan-out/fan-in appear when the recipe genuinely threads one step’s output into two later ones (or vice versa); see AGENTS.md for how that’s detected and why pipefunc (a static, declared-pipeline library) wasn’t a fit for this.

A purely-declarative recipe (no orchestration functions, no MUTABLE inputs, only paths crossing between steps) can be offloaded to a cluster with ninja compile, which emits linked sbatch scripts and, with --submit, hands the workflow off and detaches:

ninja compile myrecipe.py:pipe --target /scratch/made.ms --submit
ninja status /scratch/.shinobi/pipe/handle.json

See AGENTS.md for design conventions and what’s deliberately left out.

Status

Early scaffolding. Interfaces above are real and tested (pytest), but this is not yet ready to run real pipelines.

Installation

Once published to PyPI:

pip install stimela-ninja

Until then, install the latest from GitHub:

pip install git+https://github.com/SpheMakh/stimela-ninja.git

This installs the ninja command and the importable shinobi package.

Documentation

Full documentation is built with Sphinx and hosted on Read the Docs. Build it locally with:

uv sync --group docs
uv run sphinx-build -b html docs docs/_build/html

Development

uv venv .venv && uv pip install -e . --group dev
.venv/bin/pytest
.venv/bin/ruff check src tests

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

stimela_ninja-0.1.0b2.tar.gz (91.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

stimela_ninja-0.1.0b2-py3-none-any.whl (92.6 kB view details)

Uploaded Python 3

File details

Details for the file stimela_ninja-0.1.0b2.tar.gz.

File metadata

  • Download URL: stimela_ninja-0.1.0b2.tar.gz
  • Upload date:
  • Size: 91.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for stimela_ninja-0.1.0b2.tar.gz
Algorithm Hash digest
SHA256 b45eb39b0450f2be7ab742e7dcf0337b4e8b54ca020b75c3238fba5694adcffd
MD5 c92830246cc0dff354eb023ac6e0f38a
BLAKE2b-256 8e092e0792ce1291d315b0fc65a3d01c36bb308f06c79034398502afed1b6d2e

See more details on using hashes here.

File details

Details for the file stimela_ninja-0.1.0b2-py3-none-any.whl.

File metadata

File hashes

Hashes for stimela_ninja-0.1.0b2-py3-none-any.whl
Algorithm Hash digest
SHA256 59aa88126bd52eba44657a833105221ccee002737448327f6e84da47697b919d
MD5 6db3cba640307a36b04b79821541c5b5
BLAKE2b-256 bfd4746efc74a50d4a26e5ba912aee922abc9b9c7b3523a571a59232d28cc09f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page