magus v0.3.0 is out. See what's new
¶ View markdown source · ✎ Suggest an edit
4 min read

python

The python spell wires a Python project's tooling into a magusfile through uv. Tests, linting (ruff), and formatting run as uv run subcommands so they resolve from the project's locked environment.

Runtime name: python (source spells/python/)

Version probe (python3): python3 --version

Passing arguments to ops

Every op is invoked as python["<op>"](ctx, opts?). The first argument is the target's context, which is what carries the execution environment; the optional options map shapes the command itself:

Key Type Description Source
args [str] Extra arguments appended to the resolved command. Omit it and a bare python["<op>"]() forwards magus run <target> -- <extra> to the tool automatically; pass it to set the arguments explicitly, which replaces that passthrough. source
stdin str Data written to the command's standard input. source

Working directory and environment are NOT options: they ride the context, as python["<op>"](ctx.withCwd("sub")) and python["<op>"](ctx.withEnv({"CGO_ENABLED": "0"})). Only the context reaches the cache key, so an option-table cwd or env would change what the tool did while the key said otherwise - passing either as an option is an error.

Charms (the :charm suffix, e.g. magus run test:rw) are orthogonal: they patch the base argv, while these options add to it. See Charms.

pytest

Command: uv run pytest

debug

Appends -v.

JSON Patch
[
  {
    "op": "add",
    "path": "/-",
    "value": "-v"
  }
]

Example

// pytest runs the suite via `uv run`; here filtered to tests matching a keyword,
// so `magus run test` forks `uv run pytest -k integration`. The debug charm
// (`magus run test:debug`) adds -v.
import "magus";
import "magus/spell/python";

magus\project({ "spells": [python] });

export fun test(ctx: magus\Context, args: [str]) > void {
    python["pytest"](ctx, { "args": ["-k", "integration"] });
}

ruff-check

Command: uv run ruff check .

debug

Appends -v.

JSON Patch
[
  {
    "op": "add",
    "path": "/-",
    "value": "-v"
  }
]

gha

Inserts --output-format=github.

JSON Patch
[
  {
    "op": "add",
    "path": "/3",
    "value": "--output-format=github"
  }
]

rw

Inserts --fix.

JSON Patch
[
  {
    "op": "add",
    "path": "/3",
    "value": "--fix"
  }
]

Example

// ruff-check lints via uv run ruff; the rw charm autofixes, gha annotates in CI.
import "magus";
import "magus/spell/python";

magus\project({ "spells": [python] });

export fun lint(ctx: magus\Context, args: [str]) > void {
    python["ruff-check"](ctx);
}

ruff-format

Command: uv run ruff format --check .

rw

Drops --check.

JSON Patch
[
  {
    "op": "remove",
    "path": "/3"
  }
]

Example

// ruff-format checks formatting; the rw charm (magus run format:rw) rewrites in place.
import "magus";
import "magus/spell/python";

magus\project({ "spells": [python] });

export fun format(ctx: magus\Context, args: [str]) > void {
    python["ruff-format"](ctx);
}

scip

scip is the reserved op that runs the Python SCIP indexer for the knowledge graph. The indexer is a PATH binary (install it with mise, not as a project dep), so the op forks it directly. magus injects MAGUS_SYMBOL_INDEX with the cache destination, so the index never lands in the tree; scip-python writes there via --output. Run through sh so the env var expands.

Command: sh -c scip-python index . --output "$MAGUS_SYMBOL_INDEX"

uv-build

build/clean are uv's own subcommands; pytest and ruff are tools uv merely runs, so they are named after the tool (pytest, ruff-check), not the uv run wrapper.

Command: uv build

Example

// Wire uv-build into a build target: magus run build forks uv build.
import "magus";
import "magus/spell/python";

magus\project({ "spells": [python] });

export fun build(ctx: magus\Context, args: [str]) > void {
    python["uv-build"](ctx);
}

uv-clean

Command: uv clean

Example

// Wire uv-clean into a clean target: magus run clean forks uv clean.
import "magus";
import "magus/spell/python";

magus\project({ "spells": [python] });

export fun clean(ctx: magus\Context, args: [str]) > void {
    python["uv-clean"](ctx);
}
auto-generatedpythonspelluvpytestrufftools
Last updated (a103255f)
Earlier changes on this page (2)

Full history ↗ · Blame source ↗

Glossary

Project

A directory magus recognizes as a unit of work (it has a magusfile); the unit of caching, scheduling, and dependency tracking. See workspace.

Magusfile

The magusfile.buzz that declares a project's targets (as export funs) and binds its spells. See targets.

Target

A named operation (build, test, ...) you invoke with magus run <target>; it may compose a spell's tool-native operations and depend on other targets. See targets.

Spell

A language/runtime adapter (e.g. go, md) that maps generic targets onto a toolchain's real commands. See spells.

Charm

An execution modifier attached with : (lint:rw) that changes how a target runs, not which one; the built-in rw flips a check-only target to mutate in place, and ci always strips it. See charms.

Ward

A coded diagnostic that inspects a resolved op and nudges or blocks an anti-pattern before it runs. See wards.

Buzz

The language magusfiles are written in (the .buzz engine). See engines.

Cache

The content-addressed store magus consults before running a target, so unchanged work is skipped. See cache.

CI

An ordinary magusfile-defined target you compose yourself with magus\needs - magus does not hardcode its stages. Magus.RunCI treats it specially only in that it strips the rw charm, it is the anchor magus affected ci keys off, and a selected scope with no project declaring it is a load error rather than a silent no-op. See targets.

Knowledge graph

The queryable graph of a workspace's spells, targets, docs, and code relationships; query it with magus query/explain/path. See knowledge.

Conventions

Placeholders

Angle brackets mark a value you replace with your own - never type the brackets:

magus run <target>
magus completion <shell>    # e.g. bash, zsh, fish

<target>, <path>, <shell>, <name> and the like are stand-ins, not literal text.