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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Shared, language-agnostic operations — editing a module's dependencies or its
required engine version — are owned by the core CLI (`dagger module deps`,
`dagger module engine`) and are no longer part of this module's surface.

Backed by [`github.com/dagger/sdk-sdk/polyfill`](https://github.com/dagger/sdk-sdk/tree/main/polyfill).
It uses the engine's native `Workspace` and `ModuleSource` APIs directly.

## Install

Expand Down
11 changes: 2 additions & 9 deletions dagger.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
{
"name": "python-sdk",
"engineVersion": "v1.0.0-0",
"engineVersion": "v1.0.0-beta.10",
"sdk": {
"source": "dang"
},
"dependencies": [
{
"name": "polyfill",
"source": "github.com/dagger/polyfill@main",
"pin": "16627066d1852106320bdc0cfa0e5f901efe5970"
}
]
}
}
1 change: 0 additions & 1 deletion dagger.lock
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
[["version","1"]]
["","git.head",["https://github.com/dagger/sdk-sdk"],"e1747f4b6221fa24da080701e027243e0cc5fa33","float"]
["","git.ref",["https://github.com/dagger/polyfill","main"],"ec3ea84a2351b4beb06ecece951f2e5ef66509ff","float"]
19 changes: 9 additions & 10 deletions docs/cwd-aware-discovery.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
# CWD-aware module discovery

The Python SDK delegates config discovery to `github.com/dagger/polyfill`, then
intersects the discovered directories with `currentModule.asSDK.modules`. The
engine's managed-module list remains authoritative while the caller's current
directory determines scope.
The Python SDK asks
`currentModule.asSDK(workspace: ws).modulesInScope` for the registered modules
relevant to the caller's current directory. The engine owns both membership and
scope selection, so the SDK does not scan config files or reconstruct the cwd
policy.

Discovery returns modules at or below the cwd and, when the cwd has no module
config, its nearest enclosing module. Both `dagger-module.toml` and legacy
`dagger.json` are considered together, so the nearest config wins regardless of
filename. Virtual environments and installed packages are excluded.
Selection returns modules at or below the cwd and, when the cwd itself is not a
registered module, its nearest registered ancestor.

```console
dagger check -l
dagger call e-2-e mixed-config-lookup-check
dagger call e-2-e module-discovery-check
```

The fixtures cover mixed nested config formats, modern and legacy configs,
non-Python exclusion, root discovery, and discovery from inside a module.
The fixtures cover mixed nested config formats, modern and legacy modules,
non-Python exclusion, root selection, and selection from inside a module.
2 changes: 1 addition & 1 deletion mod-config.dang
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ type ModConfig {
}

let edited = withImage.file(toolPath).contents
polyfill.workspace(ws).fork.withNewFile(pyprojectPath, edited).changes
ws.fork.withNewFile("/" + pyprojectPath, edited).changes
}

"""
Expand Down
6 changes: 3 additions & 3 deletions mod.dang
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,12 @@ type Mod {
"""
pub generate: Changeset! {
if (skipGenerate) {
polyfill.workspace(ws).fork.changes
ws.fork.changes
} else {
# Stage the local dependency closure so this module's codegen sees
# up-to-date dependency bindings before generating it.
let stagedWs = ws.withChanges(polyfill.workspace(ws).moduleSource("/" + rootPath).core.generateLocalDependencies(ws))
polyfill.workspace(stagedWs).moduleSource("/" + rootPath).generate.changes
let stagedWs = ws.withChanges(ws.moduleSource("/" + rootPath).generateLocalDependencies(ws))
stagedWs.moduleSource("/" + rootPath).generatedContextChangeset
}
}
}
63 changes: 22 additions & 41 deletions python-sdk.dang
Original file line number Diff line number Diff line change
Expand Up @@ -17,34 +17,16 @@ type PythonSdk {
pub targetRuntime: String! { "python" }

"""
Return every managed Python SDK module visible from the client's cwd: the
nearest enclosing module plus modules at or below the cwd. Discovery uses the
shared polyfill and intersects its results with the SDK list on the passed
workspace.
Return every managed Python SDK module in the client's cwd scope: every module
at or below the cwd, plus the nearest enclosing module when the cwd itself is
not managed. The engine selects this directly from the workspace's registered
SDK modules; no filesystem discovery is required.
"""
pub modules(ws: Workspace!): [Mod!]! {
let managed = currentModule.asSDK(workspace: ws).modules.{{path}}
let cwd = normalizePath(ws.cwd)
polyfill.workspace(ws)
.findConfigDirs(moduleConfigFilenames, exclude: ["**/.venv/**", "**/site-packages/**"])
.map { dir => workspacePath(cwd, dir) }
.uniq
.filter { path => managed.filter { m => normalizePath(m.path) == path }.length > 0 }
.map { path => Mod(rootPath: path, ws: ws, skipGenerateFilename: skipGenerateFilename) }
}

let workspacePath(cwd: String!, path: String!): String! {
let base = if (cwd == ".") { [] } else { cwd.split("/") }
let segments = path.split("/").reduce(base) { acc, segment =>
if (segment == "..") {
acc.dropLast(1)
} else if (segment == "." or segment == "") {
acc
} else {
acc + [segment]
}
}
if (segments.length == 0) { "." } else { segments.join("/") }
currentModule
.asSDK(workspace: ws)
.modulesInScope.{{path}}
.map { module => Mod(rootPath: module.path, ws: ws, skipGenerateFilename: skipGenerateFilename) }
}

let normalizePath(path: String!): String! {
Expand Down Expand Up @@ -162,8 +144,8 @@ type PythonSdk {
} else {
let templateSource = configuredTemplate(renderedTemplate(name, selectedTemplate), pythonVersion, useUv, baseImage)

polyfill.workspace(ws).fork
.withDirectory(modPath, templateSource)
ws.fork
.withNewDirectory("/" + modPath, templateSource)
.changes
}
}
Expand Down Expand Up @@ -219,18 +201,17 @@ type PythonSdk {
Modules with the generate skip marker are skipped.
"""
pub generateAll(ws: Workspace!): Changeset! @generate {
let pws = polyfill.workspace(ws)

modules(ws)
.filter { mod => mod.skipGenerate == false }
.reduce(pws.fork) { fork, mod =>
# Stage this module's local dependency closure first (leaf-first, possibly
# across SDKs) so its codegen sees up-to-date dependency bindings. The dep
# codegen is ephemeral: it appears in both the fork's before and after, so
# it cancels in the merge, leaving only each module's own changes.
let stagedWs = ws.withChanges(pws.moduleSource("/" + mod.rootPath).core.generateLocalDependencies(ws))
fork.merge(polyfill.workspace(stagedWs).moduleSource("/" + mod.rootPath).generate)
}
.changes
changeset.withChangesets(
modules(ws)
.filter { mod => mod.skipGenerate == false }
.map { mod =>
# Stage this module's local dependency closure first (leaf-first, possibly
# across SDKs) so its codegen sees up-to-date dependency bindings. The dep
# codegen is ephemeral: taking the changeset against the staged workspace
# cancels it out, leaving only each module's own changes.
let stagedWs = ws.withChanges(ws.moduleSource("/" + mod.rootPath).generateLocalDependencies(ws))
stagedWs.moduleSource("/" + mod.rootPath).generatedContextChangeset
},
)
}
}