Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

A spiritual successor to Stimela classic, built around the same core philosophy: robust 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. Package-scoped includes resolve against an explicit package_roots={"cultcargo": Path(...)} mapping the caller supplies, never by importing the named package.

    Two limitations worth knowing before you evaluate this against your own cab library, both deliberate (see SECURITY.md for the reasoning, and the module docstring for detail):

    • The =config.x.y / ${...} expression language is not evaluated; such values stay literal strings.

    • dynamic_schema: is not resolved, because doing so means importing and calling a function a cab file names. A cab using it — real cult-cargo’s wsclean.yml, cubical.yml and quartical.yml all do — loads with a warning and whatever static inputs:/outputs: it has, which may be an incomplete schema. Hand-authored full ports of those three live in dosho; prefer them.

    Relatedly, only flavour: binary cabs execute. cult-cargo’s code-carrying flavours (python, inline source — e.g. msutils.copycol, bdsf.catalog) are refused with UnsupportedFlavourError rather than run.

  • 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 BackendRun – no async mode, steps are scheduled by dispatch, not left to fire-and-forget. 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; the slurm step backend has no live test yet (covered only by mocked-CLI tests) – see docs/concepts/backends.rst for the full verification status.

  • 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 ]

Nothing is executed to produce this: a Recipe is a declared graph – a list of steps plus their InputRef/OutputRef wiring, built once when the recipe module runs – and --dryrun simply renders that graph. The same validation (shinobi.graph.build_graph) backs both the renderer and the real executor, so a cyclic or mis-wired recipe is rejected identically either way, and the diagram never disagrees with what a real run would do. Steps that share the same declared dependencies render on one row (a fan-out); a step fed by several upstream outputs is a fan-in.

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 docs/design.rst for the design philosophy behind the declared-DAG model and what’s deliberately left out.

Coming from CARACal or Stimela 2?

docs/migration.rst maps a CARACal worker onto a shinobi recipe, with two real workers (transform and flag) side by side: what carries over (your cab YAML, your worker schemas), what you rewrite by hand (the worker body – enable: flags become ordinary if statements), and what has no equivalent yet. Read it before porting anything – it opens with a breaking change to package-scoped _include that you will otherwise hit first.

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/shinobi-dosho/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 sync --group dev
.venv/bin/pytest
.venv/bin/ruff check src tests

uv.lock is committed and uv sync installs exactly what it pins, which is what CI runs too (every job uses --locked). See CONTRIBUTING.md for the lockfile workflow and the repo’s pre-commit hook.

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.0b4.tar.gz (227.9 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.0b4-py3-none-any.whl (225.5 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: stimela_ninja-0.1.0b4.tar.gz
  • Upload date:
  • Size: 227.9 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.0b4.tar.gz
Algorithm Hash digest
SHA256 53ce5e61da0d6020680c078d51dc3083efc1c0e20674c0ebe961e6b18d295537
MD5 ab60199736887e852b0d79f514ca1efe
BLAKE2b-256 c7bc0e876b31d5d05f1b16d3566138dde21521237047f715214f859feed6f4ea

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for stimela_ninja-0.1.0b4-py3-none-any.whl
Algorithm Hash digest
SHA256 cf5edc2949fae288ab3652055f4a79cd5f4690701feae1ec9ed719d1e4562166
MD5 395f8843412d07b05e89b8c237b2ccfb
BLAKE2b-256 0e215bacdf0c6dfaca84a3ed226f2c9e6e9a7bd08e7463856991d53b3aa78b12

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.0b4 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page