22Manage Dagger modules that use the Python SDK.
33"""
44type PythonSdk {
5+ let legacyConfigPattern = "\"sdk\"\\s*:\\s*\\{[^}]*\"source\"\\s*:\\s*\"python\""
6+ let tomlConfigPattern = "\\[runtime\\][^\\[]*source\\s*=\\s*\"python\""
7+ let moduleConfigFilenames: [String!]! = ["dagger-module.toml", "dagger.json"]
8+
59 """
610 Marker filename that skips generate when found at or above a Python SDK module root.
711 """
@@ -13,29 +17,46 @@ type PythonSdk {
1317 pub targetRuntime: String! { "python" }
1418
1519 """
16- Return every legacy dagger.json Dagger module whose sdk.source is "python".
17-
18- This discovery path is obsolete for workspace-managed modules; the engine
19- owns the modules.<sdk>.as-sdk.modules source of truth.
20+ Return every managed Python SDK module visible from the client's cwd: the
21+ nearest enclosing module plus modules at or below the cwd. Discovery uses the
22+ shared polyfill and intersects its results with the engine-owned as-SDK list.
2023 """
2124 pub modules(ws: Workspace!): [Mod!]! {
22- ws
23- .directory("/", include: ["**/dagger.json"])
24- .search(
25- pattern: "\"sdk\"\\s*:\\s*\\{[^}]*\"source\"\\s*:\\s*\"python\"",
26- globs: ["**/dagger.json"],
27- filesOnly: true,
28- multiline: true,
29- dotall: true,
30- )
31- .{{filePath}}
32- .map { configPath =>
33- mod(
34- ws,
35- if (configPath.filePath == "dagger.json") { "." } else { configPath.filePath.trimSuffix("/dagger.json") },
36- findUp: false,
37- )
25+ let managed = currentModule.asSDK.modules.{{path}}
26+ let cwd = normalizePath(ws.cwd)
27+ polyfill.workspace(ws)
28+ .findConfigDirs(moduleConfigFilenames, exclude: ["**/.venv/**", "**/site-packages/**"])
29+ .map { dir => workspacePath(cwd, dir) }
30+ .uniq
31+ .filter { path => managed.filter { m => normalizePath(m.path) == path }.length > 0 }
32+ .map { path => Mod(rootPath: path, ws: ws, skipGenerateFilename: skipGenerateFilename) }
33+ }
34+
35+ let workspacePath(cwd: String!, path: String!): String! {
36+ let base = if (cwd == ".") { [] } else { cwd.split("/") }
37+ let segments = path.split("/").reduce(base) { acc, segment =>
38+ if (segment == "..") {
39+ acc.dropLast(1)
40+ } else if (segment == "." or segment == "") {
41+ acc
42+ } else {
43+ acc + [segment]
3844 }
45+ }
46+ if (segments.length == 0) { "." } else { segments.join("/") }
47+ }
48+
49+ let normalizePath(path: String!): String! {
50+ let normalized = path.trimPrefix("./").trimPrefix("/").trimSuffix("/")
51+ if (normalized == "") { "." } else { normalized }
52+ }
53+
54+ let pathDepth(path: String): Int! {
55+ if (path == null) { -1 } else if (path == ".") { 0 } else { path.split("/").length }
56+ }
57+
58+ let configDir(path: String!, filename: String!): String! {
59+ normalizePath(path.trimPrefix("/").trimSuffix(filename))
3960 }
4061
4162 """
@@ -71,42 +92,42 @@ type PythonSdk {
7192 """
7293 pub mod(ws: Workspace!, path: String! = ".", findUp: Boolean! = true): Mod! {
7394 let modPath = if (findUp) {
74- let foundConfigPath = ws.findUp("dagger.json", path)
75- if (foundConfigPath == null) {
95+ let foundToml = ws.findUp("dagger-module.toml", path)
96+ let foundJson = ws.findUp("dagger.json", path)
97+ let tomlBase = if (foundToml == null) { null } else { configDir(foundToml, "dagger-module.toml") }
98+ let jsonBase = if (foundJson == null) { null } else { configDir(foundJson, "dagger.json") }
99+
100+ if (tomlBase == null and jsonBase == null) {
76101 raise "no Dagger module found containing path: " + path
102+ } else if (tomlBase != null and pathDepth(tomlBase) > pathDepth(jsonBase)) {
103+ validateConfig(ws, tomlBase, "dagger-module.toml", tomlConfigPattern, path)
104+ } else if (jsonBase != null) {
105+ validateConfig(ws, jsonBase, "dagger.json", legacyConfigPattern, path)
77106 } else {
78- let configPath = foundConfigPath.trimPrefix("/")
79- if (
80- ws
81- .directory("/", include: [configPath])
82- .file(configPath)
83- .search(
84- pattern: "\"sdk\"\\s*:\\s*\\{[^}]*\"source\"\\s*:\\s*\"python\"",
85- multiline: true,
86- dotall: true,
87- limit: 1,
88- )
89- .{{id}}
90- .length == 0
91- ) {
92- raise "Dagger module does not use the Python SDK: " + path
93- } else if (configPath == "dagger.json") {
94- "."
95- } else {
96- configPath.trimSuffix("/dagger.json")
97- }
107+ raise "no Dagger module found containing path: " + path
98108 }
99109 } else {
100- path.trimPrefix("/" )
110+ normalizePath(path )
101111 }
102112
103113 Mod(
104- path : modPath,
114+ rootPath : modPath,
105115 ws: ws,
106116 skipGenerateFilename: skipGenerateFilename,
107117 )
108118 }
109119
120+ let validateConfig(ws: Workspace!, base: String!, filename: String!, pattern: String!, requestedPath: String!): String! {
121+ let configPath = if (base == ".") { filename } else { base + "/" + filename }
122+ let matches = ws
123+ .directory("/", include: [configPath])
124+ .file(configPath)
125+ .search(pattern: pattern, multiline: true, dotall: true, limit: 1)
126+ .{{id}}
127+ .length > 0
128+ if (matches) { base } else { raise "Dagger module does not use the Python SDK: " + requestedPath }
129+ }
130+
110131 """
111132 Initialize Python-owned files for a new Dagger module.
112133
@@ -191,22 +212,17 @@ type PythonSdk {
191212 }
192213
193214 """
194- Generate all discovered legacy dagger.json Python SDK modules.
195-
196- This discovery path is obsolete for workspace-managed modules; the engine
197- owns the modules.<sdk>.as-sdk.modules source of truth.
215+ Generate every managed Python SDK module visible from the client's cwd.
198216
199217 Modules with the generate skip marker are skipped.
200218 """
201219 pub generateAll(ws: Workspace!): Changeset! @generate {
202220 let pws = polyfill.workspace(ws)
203221
204- currentModule.asSDK.modules
205- .{{path}}
206- .map { m => Mod(path: m.path, ws: ws, skipGenerateFilename: skipGenerateFilename) }
222+ modules(ws)
207223 .filter { mod => mod.skipGenerate == false }
208224 .reduce(pws.fork) { fork, mod =>
209- fork.merge(pws.moduleSource("/" + mod.path ).generate)
225+ fork.merge(pws.moduleSource("/" + mod.rootPath ).generate)
210226 }
211227 .changes
212228 }
0 commit comments