Skip to content

Repository files navigation

python-sdk

A Dagger module for managing Dagger modules that use the Python SDK.

SDK-specific module authoring (scaffolding new modules, language build config, codegen) lives in modules like this one. The engine drives the SDK (dagger/dagger#13992): it records a module scope in dagger.toml, sets the workspace cwd to it, and asks this module to generate the complete scope through detectScope and generateScope. The module writes the manifest and its own files; the engine owns the workspace bookkeeping.

It uses the engine's native Workspace and ModuleSource APIs directly.

What lives here

Path What it is
python-sdk.dang, mod.dang, templates/ authoring: detectScope, generateScope, mod (generate, config), templates
sdk/ the dagger-io client library and code generator
runtime/ the module runtime the engine calls to run a module

Code generation happens at dagger generate, which calls generateScope for every recorded scope. It runs the code generator in sdk/ and vendors the result into the module. The runtime never generates: it builds a module from its committed generated files, so there is no codegen step in a cold dagger call, and a module that has not been generated fails with an actionable error rather than being silently regenerated.

Pre-1.0 dagger.json modules are the exception: they keep being generated and run by the Python SDK baked into the engine, exactly as before.

Two runtimes, one name

Python modules reach one of two implementations, and which one is decided by the module's config format:

  • Legacy — a dagger.json with "sdk": {"source": "python"} resolves to the runtime baked into the engine (dagger/dagger's sdk/python), which still generates bindings at module load. Nothing about those modules changes, and they need no migration.
  • Modern — a dagger-module.toml can point [runtime] source at this repository's runtime/, which is the no-codegen path above. Either a module ref or a path relative to the module works, for both dagger generate and dagger call.

The engine resolves the short name python to exactly one target, the engine-baked runtime, so the modern path is reached by module ref rather than by name. The manifest generateScope writes for a new module therefore still names python; it moves to github.com/dagger/python-sdk/runtime in a follow-up, once runtime/ exists on the default branch for that ref to resolve to. See future/done/self-contained-python-sdk.md for the full reasoning and for the engine change that would let one name serve both.

Trying this repository's runtime

A module created today names the python runtime, so it runs on the engine's runtime. To move one onto this repository's runtime, point it there by hand:

# <module>/dagger-module.toml
[runtime]
source = "github.com/dagger/python-sdk/runtime"

Then dagger generate the module and dagger call it as usual. The generated files are identical either way — generation is this SDK's regardless of which runtime runs the module — so switching back is just editing the line again.

Within this repository, a path relative to the module works too, which is how the end-to-end fixture exercises the runtime before the ref exists.

Install

From your workspace root:

dagger module install github.com/dagger/python-sdk

The engine recognizes the SDK interface and records the module as the python SDK in dagger.toml. After install, the module is also available in dagger call as python-sdk.

Calls that return a Changeset will print the diff and prompt you to confirm before writing anything to your workspace.

Create a new module

dagger module init python --name my-module

The engine records the module scope in dagger.toml and calls this SDK's generateScope, which renders the template, writes dagger-module.toml, and generates the SDK bindings in one step.

The SDK settings below become typed flags on dagger module init python and are persisted on the scope:

dagger module init python --name my-module --template legacy
dagger module init python --name my-module \
    --python-version 3.13 \
    --use-uv=false \
    --base-image python:3.13-slim

--template picks a starter template: default (a small working module) when you pass nothing, empty for a bare object class, or legacy for a container-echo example. The three pyproject.toml flags are optional; by default the template's Python version is used, uv is enabled, and no base image override is written.

Configure an existing module

Read the current configuration. Settings that are not explicitly written to pyproject.toml are reported as null rather than guessed:

dagger call python-sdk mod --path my-module config get

Select a single value:

dagger call python-sdk mod --path my-module config get python-version
dagger call python-sdk mod --path my-module config get use-uv
dagger call python-sdk mod --path my-module config get base-image

Change one or more values at once (prints a diff to confirm before writing). Each flag is optional; omitting one leaves that setting untouched:

dagger call python-sdk mod --path my-module config set \
    --python-version 3.13 \
    --use-uv=false \
    --base-image python:3.13-slim

Generate SDK files

dagger generate regenerates every recorded scope. A recorded module can also be generated on its own:

dagger call python-sdk mod --path my-module generate

mod resolves recorded modules by default. For a module root that is not recorded, pass the module root as --path and add --find-up=false:

dagger call python-sdk mod --path my-module --find-up=false generate

Module clients

Module dependencies are replaced by generated module clients (dagger module client add). In a module scope the client set becomes the module's dependency set: each client is recorded in dagger-module.toml and its types are part of the generated bindings, and a removed client is dropped again. Standalone clients, in a scope without a module, are not generated yet; adding one to a Python scope is refused and the workspace is left unchanged.

Skipping generation

A .dagger-python-sdk-skip-generate file at or above an existing module root makes dagger generate and mod generate leave that module as it is. Useful for fixtures, vendored modules, or anything you don't want regenerated. A new module is always generated.

touch some/fixture/.dagger-python-sdk-skip-generate

Test

dagger check

engine-e-2-e:dev-sdk-check builds an engine from dagger/dagger#13992 at the commit pinned in .dagger/modules/engine-e2e (the engine-dev dependency and engineCommit), installs this checkout as the python SDK, initializes a Python module, and calls it. Bump both to follow the branch. The e-2-e:* checks that call this module need an engine with that change as well; on the released engine they fail, because the module selects moduleManifest, which that engine does not have.

About

Develop Dagger modules in Python

Resources

Code of conduct

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages