Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .dagger/modules/e2e/main.dang
Original file line number Diff line number Diff line change
Expand Up @@ -225,4 +225,34 @@ type E2e {

null
}

"""
Workspace-scoped SDK settings (the PythonSdk constructor args, surfaced by
`dagger settings python-sdk`) should act as init defaults: when an init flag
is omitted, initModule inherits the constructor value, and an explicit
per-init flag still wins.
"""
pub initSettingsDefaultCheck(ws: Workspace!): Void @check {
let configuredSdk = pythonSdk(defaultPythonVersion: "3.13", defaultUseUv: false, defaultBaseImage: "python:3.13-slim")

# Flags omitted: the constructor (workspace) defaults flow into pyproject.toml.
let inheritedPath = outputRoot + "/init-settings-inherited"
let inherited = configuredSdk.initModule(ws, name: "init-settings-inherited", path: inheritedPath)
let inheritedPyproj = inheritedPath + "/pyproject.toml"
assertAdded(inherited, inheritedPyproj)
assertContains(inherited.layer.file(inheritedPyproj).contents, ">=3.13", "default-python-version setting not applied at init")
assertContains(inherited.layer.file(inheritedPyproj).contents, "use-uv = false", "default-use-uv setting not applied at init")
assertContains(inherited.layer.file(inheritedPyproj).contents, "python:3.13-slim", "default-base-image setting not applied at init")
assertContains(inherited.layer.file(inheritedPyproj).contents, "dagger-io", "settings-default init dropped template data")

# Explicit per-init flags override the workspace defaults.
let overridePath = outputRoot + "/init-settings-override"
let override = configuredSdk.initModule(ws, name: "init-settings-override", path: overridePath, pythonVersion: "3.12", useUv: true, baseImage: "python:3.12-slim")
let overridePyproj = overridePath + "/pyproject.toml"
assertContains(override.layer.file(overridePyproj).contents, ">=3.12", "per-init python version did not override default-python-version")
assertContains(override.layer.file(overridePyproj).contents, "python:3.12-slim", "per-init base image did not override default-base-image")
assertNotContains(override.layer.file(overridePyproj).contents, "use-uv = false", "per-init use-uv=true did not override default-use-uv=false")

null
}
}
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,36 @@ engine supplies it in the dispatched path):
dagger call python-sdk init-module --name my-module --path .dagger/modules/my-module
```

## Configure workspace defaults

Set SDK defaults once per workspace and have them apply to the modules you
create. List the settings this SDK exposes:

```sh
dagger settings python-sdk
```

Set a default (the SDK's constructor arguments are the settings; camelCase maps
to kebab-case on the CLI):

```sh
dagger settings python-sdk default-python-version 3.13
dagger settings python-sdk default-use-uv false
dagger settings python-sdk default-base-image python:3.13-slim
```

These act as fallbacks for `initModule`: when you create a module without the
matching `dagger module init python` flag (`--python-version`, `--use-uv`,
`--base-image`), the workspace default is applied. An explicit per-init flag
always wins.

> [!NOTE]
> Settings are stored under `[modules.python-sdk.settings]` in `dagger.toml`,
> are discoverable and typed today, and populate the SDK constructor on the
> `dagger call python-sdk …` path. Inheriting them through the privileged
> `dagger module init python` dispatch additionally requires the engine to
> thread `[modules.python-sdk.settings]` into the SDK at init time.

## Configure an existing module

Read the current configuration. Settings that are not explicitly written to
Expand Down
55 changes: 52 additions & 3 deletions python-sdk.dang
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,48 @@ type PythonSdk {
"""
pub targetRuntime: String! = "python"

"""
Workspace default Python version applied to modules this SDK creates.

Surfaced as the `default-python-version` setting of
`dagger settings python-sdk`. Empty leaves the template's Python version
untouched. `initModule` falls back to this when `--python-version` is omitted.
"""
pub defaultPythonVersion: String!

"""
Workspace default for building modules with uv (true) instead of pip.

Surfaced as the `default-use-uv` setting of `dagger settings python-sdk`.
`initModule` falls back to this when `--use-uv` is omitted.
"""
pub defaultUseUv: Boolean!

"""
Workspace default base container image for modules this SDK creates.

Surfaced as the `default-base-image` setting of `dagger settings python-sdk`.
Empty writes no base image override. `initModule` falls back to this when
`--base-image` is omitted.
"""
pub defaultBaseImage: String!

"""
Construct the SDK with its workspace-scoped settings.

The engine populates these constructor arguments from
`[modules.python-sdk.settings]`, and `dagger settings python-sdk` surfaces
them as typed settings (camelCase maps to kebab-case on the CLI:
default-python-version, default-use-uv, default-base-image). They act as
defaults that `initModule` applies when the matching per-init flag is omitted.
"""
new(defaultPythonVersion: String! = "", defaultUseUv: Boolean! = true, defaultBaseImage: String! = "") {
self.defaultPythonVersion = defaultPythonVersion
self.defaultUseUv = defaultUseUv
self.defaultBaseImage = defaultBaseImage
self
}

"""
Return every legacy dagger.json Dagger module whose sdk.source is "python".

Expand Down Expand Up @@ -118,10 +160,12 @@ type PythonSdk {
default uses this module's minimal template.

Pass `pythonVersion`, `useUv`, or `baseImage` to customize the generated
pyproject.toml. Defaults leave the template untouched: the template's Python
pyproject.toml. When one is omitted, the matching workspace setting
(`default-python-version`, `default-use-uv`, `default-base-image`) applies. If
that is also unset the template is left untouched: the template's Python
version is used, uv is enabled, and no base image override is written.
"""
pub initModule(ws: Workspace!, name: String!, path: String!, template: String! = "", pythonVersion: String! = "", useUv: Boolean! = true, baseImage: String! = ""): Changeset! {
pub initModule(ws: Workspace!, name: String!, path: String!, template: String! = "", pythonVersion: String! = "", useUv: Boolean = null, baseImage: String! = ""): Changeset! {
let rawPath = path.trimPrefix("./").trimPrefix("/")

let modPath = if (rawPath == "" or rawPath == ".") {
Expand All @@ -137,7 +181,12 @@ type PythonSdk {
if (currentModule.source.exists("templates/" + selectedTemplate) == false) {
raise "unknown init template: " + template
} else {
let templateSource = configuredTemplate(renderedTemplate(name, selectedTemplate), pythonVersion, useUv, baseImage)
# Per-init flags win; otherwise fall back to the workspace-scoped defaults.
let effectivePythonVersion = if (pythonVersion == "") { defaultPythonVersion } else { pythonVersion }
let effectiveUseUv = if (useUv != null) { useUv } else { defaultUseUv }
let effectiveBaseImage = if (baseImage == "") { defaultBaseImage } else { baseImage }

let templateSource = configuredTemplate(renderedTemplate(name, selectedTemplate), effectivePythonVersion, effectiveUseUv, effectiveBaseImage)

polyfill.workspace(ws).fork
.withDirectory(modPath, templateSource)
Expand Down