Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Step Overrides

What you’ll learn

  • Override operation parameters, names, and execution config per step

  • Control failure handling with FailurePolicy

  • Understand override precedence (pipeline defaults → operation defaults → step overrides)

Prerequisites: First Pipeline, Batching and Performance. Estimated time: 15 minutes GPU required: No.


Every pipeline.run() call accepts optional overrides that customize how the step executes. This tutorial demonstrates each override parameter with working examples.

from __future__ import annotations

from artisan.operations.examples import (
    DataGenerator,
    DataTransformer,
    MetricCalculator,
)
from artisan.orchestration import Backend, PipelineManager
from artisan.schemas.enums import FailurePolicy
from artisan.utils import tutorial_setup
from artisan.visualization import build_macro_graph, inspect_pipeline
env = tutorial_setup("step_overrides")

Override parameters

Each pipeline.run() call accepts override parameters for execution, resources, and backend. For the complete list, see Configuring Execution.

The most commonly used overrides are params, name, execution, backend, resources, and failure_policy. This tutorial demonstrates each one with working examples.

params — operation parameters

The params dict is passed directly to the operation. Each operation defines its own parameter schema. Passing different params to the same operation creates distinct steps with different behavior.

pipeline = PipelineManager.create(
    name="params_demo",
    delta_root=env.delta_root,
    staging_root=env.staging_root,
    working_root=env.working_root,
)

# Same operation, different params → different outputs
pipeline.run(
    operation=DataGenerator,
    name="small",
    params={"count": 3, "seed": 42},
    backend=Backend.LOCAL,
)
pipeline.run(
    operation=DataGenerator,
    name="large",
    params={"count": 10, "seed": 99},
    backend=Backend.LOCAL,
)

pipeline.finalize()
inspect_pipeline(env.delta_root)

Both steps use DataGenerator but produce different outputs: step 0 generates 3 datasets with seed 42, step 1 generates 10 with seed 99. The pipeline overview shows the different artifact counts.

name — custom step name

By default, each step is named after the operation (e.g., data_generator). Use name to give steps descriptive labels, especially when the same operation appears multiple times.

env_name = tutorial_setup("step_names")

pipeline = PipelineManager.create(
    name="name_demo",
    delta_root=env_name.delta_root,
    staging_root=env_name.staging_root,
    working_root=env_name.working_root,
)
output = pipeline.output

pipeline.run(
    operation=DataGenerator,
    name="initial_candidates",
    params={"count": 5, "seed": 42},
    backend=Backend.LOCAL,
)
pipeline.run(
    operation=DataTransformer,
    name="normalize",
    inputs={"dataset": output("initial_candidates", "datasets")},
    backend=Backend.LOCAL,
)
pipeline.run(
    operation=DataTransformer,
    name="augment",
    inputs={"dataset": output("normalize", "dataset")},
    params={"seed": 100},
    backend=Backend.LOCAL,
)

pipeline.finalize()
inspect_pipeline(env_name.delta_root)

The pipeline overview now shows initial_candidates, normalize, and augment instead of generic operation names. Custom names make pipelines easier to read, especially in provenance graphs.

build_macro_graph(env_name.delta_root)

execution — batching configuration

The execution dict accepts any ExecutionConfig field. See Batching and Performance for a deep dive.

env_exec = tutorial_setup("step_execution")

pipeline = PipelineManager.create(
    name="execution_demo",
    delta_root=env_exec.delta_root,
    staging_root=env_exec.staging_root,
    working_root=env_exec.working_root,
)
output = pipeline.output

pipeline.run(
    operation=DataGenerator,
    name="generate",
    params={"count": 12, "seed": 42},
    backend=Backend.LOCAL,
)

# Override batching: 4 artifacts per unit, max 2 concurrent workers
pipeline.run(
    operation=MetricCalculator,
    name="metrics",
    inputs={"dataset": output("generate", "datasets")},
    execution={"artifacts_per_unit": 4, "max_workers": 2},
    backend=Backend.LOCAL,
)

pipeline.finalize()
inspect_pipeline(env_exec.delta_root)

Step 1 groups 12 artifacts into 3 execution units (4 per unit) and runs at most 2 concurrently. Without the override, MetricCalculator’s default artifacts_per_unit of 10,000 would put all 12 artifacts into a single unit.

backend — execution backend

The backend parameter overrides the pipeline-level default backend for a single step. This allows mixing backends within one pipeline — for example, running lightweight steps locally while submitting heavy computation to SLURM.

# Pipeline default is SLURM, but run this step locally
pipeline.run(
    MetricCalculator,
    inputs={"dataset": output("generate", "datasets")},
    backend=Backend.LOCAL,  # Override for this step only
)

Available backends: Backend.LOCAL (process pool on current machine), Backend.SLURM (submits jobs to a SLURM cluster), and Backend.SLURM_INTRA (distributes work via srun within an existing SLURM allocation).

resources — SLURM resource allocation

The resources dict controls SLURM job resources (CPUs, memory, GPUs, time limit). These have no effect when running locally. For all ResourceConfig fields, see Configuring Execution.

pipeline.run(
    MyGPUOperation,
    inputs={"data": datasets},
    resources={"gpus": 1, "memory_gb": 32, "extra": {"partition": "gpu"}},
    backend=Backend.SLURM,
)

failure_policy — handling failures

By default, the pipeline uses the policy set at creation time (default: FailurePolicy.CONTINUE). Override per-step for fine-grained control.

PolicyBehavior
FailurePolicy.CONTINUELog failures, commit successful results, report failures in StepResult
FailurePolicy.FAIL_FASTStop on first failure, raise exception, no commit
env_fp = tutorial_setup("step_failure_policy")

pipeline = PipelineManager.create(
    name="failure_demo",
    delta_root=env_fp.delta_root,
    staging_root=env_fp.staging_root,
    working_root=env_fp.working_root,
    failure_policy=FailurePolicy.CONTINUE,  # Pipeline default
)
output = pipeline.output

pipeline.run(
    operation=DataGenerator,
    name="generate",
    params={"count": 5, "seed": 42},
    backend=Backend.LOCAL,
)

# This step must succeed completely — fail fast on any error
pipeline.run(
    operation=DataTransformer,
    name="transform",
    inputs={"dataset": output("generate", "datasets")},
    failure_policy=FailurePolicy.FAIL_FAST,
    backend=Backend.LOCAL,
)

# This step tolerates partial failures — continue processing
pipeline.run(
    operation=MetricCalculator,
    name="metrics",
    inputs={"dataset": output("transform", "dataset")},
    failure_policy=FailurePolicy.CONTINUE,
    backend=Backend.LOCAL,
)

pipeline.finalize()
inspect_pipeline(env_fp.delta_root)

Step 1 uses FAIL_FAST — if any execution unit fails, the entire step aborts with an exception. Step 2 uses CONTINUE — failures are logged and surviving results are committed. Use FAIL_FAST for critical steps where partial results are meaningless, and CONTINUE for steps where you’d rather keep what succeeded.

environment — container and environment configuration

The environment parameter selects which execution environment to use for a step. Pass a string to select a pre-configured environment, or a dict to override environment settings.

Available environments: "local", "docker", "apptainer", "pixi".

# Run this step in a Docker container
pipeline.run(
    MyOperation,
    inputs={"data": datasets},
    environment={
        "active": "docker",
        "docker": {"image": "my_image:latest"},
    },
)

Since environment overrides require specific container or environment setups, this tutorial does not include a runnable example.

tool — external tool overrides

The tool dict overrides the executable, interpreter, or subcommand for operations that wrap external programs.

FieldTypeDescription
executablestrPath or name of the binary/script
interpreterstrInterpreter prefix (e.g. "python")
subcommandstrSubcommand inserted after the executable
pipeline.run(
    ToolAOp,
    inputs={"data": datasets},
    tool={"executable": "/opt/tool_a/bin/tool_a"},
)

The operation must already define a tool in its class — this override only changes specific fields.

Override precedence

When the same setting is specified at multiple levels, step-level overrides win:

Pipeline defaults  →  Operation class defaults  →  Step overrides (wins)

For example, if the pipeline default failure policy is CONTINUE, the operation class has no override, and a step specifies FAIL_FAST, that step uses FAIL_FAST.

This applies to all override parameters: backend, resources, execution, environment, tool, and failure_policy. The params parameter has no defaults — it is always explicit per step.

Summary

OverrideDefaultWhen to use
params{}Always — operation-specific configuration
nameOperation nameWhen the same operation appears multiple times
executionOperation defaultsTuning batching for specific steps
backendPipeline defaultMixing local and SLURM execution
resourcesOperation defaultsSLURM resource tuning (memory, GPUs, time limit)
failure_policyPipeline defaultCritical steps that must fully succeed
environmentOperation defaultsContainer or environment selection
toolOperation defaultsExternal tool executable overrides
compactTrueFast back-to-back steps where compaction overhead matters

Key takeaway: Step overrides give you fine-grained control without changing operation code. Start with pipeline-level defaults, and override individual steps only where needed.

Next steps