@@ -37,13 +37,20 @@ const (
37
37
// most examples use 5678
38
38
defaultPtvsdPort = 5678
39
39
defaultDebugpyPort = 5678
40
+ defaultPydevdPort = 5678
40
41
)
41
42
42
43
type pythonDebugType int
43
44
44
45
const (
45
46
ptvsd pythonDebugType = iota
46
47
debugpy
48
+ pydevd
49
+ )
50
+
51
+ const (
52
+ pydevdProtocol = "pydevd"
53
+ dapProtocol = "dap"
47
54
)
48
55
49
56
// pythonSpec captures the useful python-ptvsd devtools options
@@ -56,58 +63,84 @@ type pythonSpec struct {
56
63
57
64
// isLaunchingPython determines if the arguments seems to be invoking python
58
65
func isLaunchingPython (args []string ) bool {
59
- return len (args ) > 0 &&
60
- (args [0 ] == "python" || strings .HasSuffix (args [0 ], "/python" ) ||
61
- args [0 ] == "python2" || strings .HasSuffix (args [0 ], "/python2" ) ||
62
- args [0 ] == "python3" || strings .HasSuffix (args [0 ], "/python3" ))
66
+ return len (args ) > 0 && (args [0 ] == "python" || strings .HasSuffix (args [0 ], "/python" ) ||
67
+ args [0 ] == "python2" || strings .HasSuffix (args [0 ], "/python2" ) ||
68
+ args [0 ] == "python3" || strings .HasSuffix (args [0 ], "/python3" ))
69
+ }
70
+
71
+ func hasCommonPythonEnvVars (env map [string ]string ) bool {
72
+ for _ , key := range []string {
73
+ "PYTHON_VERSION" ,
74
+ "PYTHONVERBOSE" ,
75
+ "PYTHONINSPECT" ,
76
+ "PYTHONOPTIMIZE" ,
77
+ "PYTHONUSERSITE" ,
78
+ "PYTHONUNBUFFERED" ,
79
+ "PYTHONPATH" ,
80
+ "PYTHONUSERBASE" ,
81
+ "PYTHONWARNINGS" ,
82
+ "PYTHONHOME" ,
83
+ "PYTHONCASEOK" ,
84
+ "PYTHONIOENCODING" ,
85
+ "PYTHONHASHSEED" ,
86
+ "PYTHONDONTWRITEBYTECODE" ,
87
+ } {
88
+ if _ , found := env [key ]; found {
89
+ return true
90
+ }
91
+ }
92
+
93
+ return false
63
94
}
64
95
65
96
func (t pythonTransformer ) IsApplicable (config imageConfiguration ) bool {
66
- // We can only put Python in debug mode by modifying the python command line,
67
- // so looking for Python-related environment variables is insufficient.
97
+ if hasCommonPythonEnvVars (config .env ) {
98
+ return true
99
+ }
100
+
68
101
if len (config .entrypoint ) > 0 && ! isEntrypointLauncher (config .entrypoint ) {
69
102
return isLaunchingPython (config .entrypoint )
70
103
}
71
104
return isLaunchingPython (config .arguments )
72
105
}
73
106
74
- // Apply configures a container definition for Python with ptvsd/debugpy.
107
+ // Apply configures a container definition for Python with ptvsd/debugpy/pydevd .
75
108
// Returns a simple map describing the debug configuration details.
76
- func (t pythonTransformer ) Apply (container * v1.Container , config imageConfiguration , portAlloc portAllocator ) (ContainerDebugConfiguration , string , error ) {
109
+ func (t pythonTransformer ) Apply (container * v1.Container , config imageConfiguration , portAlloc portAllocator , overrideProtocols [] string ) (ContainerDebugConfiguration , string , error ) {
77
110
logrus .Infof ("Configuring %q for python debugging" , container .Name )
78
111
79
112
// try to find existing `-mptvsd` or `-mdebugpy` command
80
113
if spec := retrievePythonDebugSpec (config ); spec != nil {
81
- container .Ports = exposePort (container .Ports , "dap" , spec .port )
114
+ protocol := spec .protocol ()
115
+ container .Ports = exposePort (container .Ports , protocol , spec .port )
82
116
return ContainerDebugConfiguration {
83
117
Runtime : "python" ,
84
- Ports : map [string ]uint32 {"dap" : uint32 (spec .port )},
118
+ Ports : map [string ]uint32 {protocol : uint32 (spec .port )},
85
119
}, "" , nil
86
120
}
87
121
88
- spec := & pythonSpec {debugger : debugpy , port : portAlloc (defaultDebugpyPort )}
122
+ spec := createPythonDebugSpec (overrideProtocols , portAlloc )
123
+
89
124
switch {
90
125
case isLaunchingPython (config .entrypoint ):
91
126
container .Command = rewritePythonCommandLine (config .entrypoint , * spec )
92
127
93
128
case (len (config .entrypoint ) == 0 || isEntrypointLauncher (config .entrypoint )) && isLaunchingPython (config .arguments ):
94
129
container .Args = rewritePythonCommandLine (config .arguments , * spec )
95
130
131
+ case hasCommonPythonEnvVars (config .env ):
132
+ container .Command = rewritePythonCommandLine (config .entrypoint , * spec )
133
+
96
134
default :
97
135
return ContainerDebugConfiguration {}, "" , fmt .Errorf ("%q does not appear to invoke python" , container .Name )
98
136
}
99
137
100
- pyUserBase := "/dbg/python"
101
- if existing , found := config .env ["PYTHONUSERBASE" ]; found {
102
- // todo: handle windows containers?
103
- pyUserBase = pyUserBase + ":" + existing
104
- }
105
- container .Env = setEnvVar (container .Env , "PYTHONUSERBASE" , pyUserBase )
106
- container .Ports = exposePort (container .Ports , "dap" , spec .port )
138
+ protocol := spec .protocol ()
139
+ container .Ports = exposePort (container .Ports , protocol , spec .port )
107
140
108
141
return ContainerDebugConfiguration {
109
142
Runtime : "python" ,
110
- Ports : map [string ]uint32 {"dap" : uint32 (spec .port )},
143
+ Ports : map [string ]uint32 {protocol : uint32 (spec .port )},
111
144
}, "python" , nil
112
145
}
113
146
@@ -131,6 +164,19 @@ func extractPythonDebugSpec(args []string) *pythonSpec {
131
164
return nil
132
165
}
133
166
167
+ func createPythonDebugSpec (overrideProtocols []string , portAlloc portAllocator ) * pythonSpec {
168
+ for _ , p := range overrideProtocols {
169
+ switch p {
170
+ case pydevdProtocol :
171
+ return & pythonSpec {debugger : pydevd , port : portAlloc (defaultPydevdPort )}
172
+ case dapProtocol :
173
+ return & pythonSpec {debugger : debugpy , port : portAlloc (defaultDebugpyPort )}
174
+ }
175
+ }
176
+
177
+ return & pythonSpec {debugger : debugpy , port : portAlloc (defaultDebugpyPort )}
178
+ }
179
+
134
180
func extractPtvsdSpec (args []string ) * pythonSpec {
135
181
if ! hasPyModule ("ptvsd" , args ) {
136
182
return nil
@@ -212,42 +258,45 @@ func hasPyModule(module string, args []string) bool {
212
258
return false
213
259
}
214
260
215
- // rewritePythonCommandLine rewrites a python command-line to insert a `-mptvsd` etc
261
+ // rewritePythonCommandLine rewrites a python command-line to use the debug-support's launcher.
216
262
func rewritePythonCommandLine (commandLine []string , spec pythonSpec ) []string {
217
263
// Assumes that commandLine[0] is "python" or "python3" etc
218
- return util .StrSliceInsert (commandLine , 1 , spec .asArguments ())
264
+ return util .StrSliceInsert (commandLine , 0 , spec .asArguments ())
219
265
}
220
266
221
267
func (spec pythonSpec ) asArguments () []string {
268
+ args := []string {"/dbg/python/launcher" , "--mode" , spec .launcherMode ()}
269
+ if spec .port >= 0 {
270
+ args = append (args , "--port" , strconv .FormatInt (int64 (spec .port ), 10 ))
271
+ }
272
+ if spec .wait {
273
+ args = append (args , "--wait" )
274
+ }
275
+ args = append (args , "--" )
276
+ return args
277
+ }
278
+
279
+ func (spec pythonSpec ) launcherMode () string {
222
280
switch spec .debugger {
281
+ case pydevd :
282
+ return "pydevd"
223
283
case ptvsd :
224
- args := []string {"-mptvsd" }
225
- // --host is a mandatory argument
226
- if spec .host == "" {
227
- args = append (args , "--host" , "0.0.0.0" )
228
- } else {
229
- args = append (args , "--host" , spec .host )
230
- }
231
- if spec .port >= 0 {
232
- args = append (args , "--port" , strconv .FormatInt (int64 (spec .port ), 10 ))
233
- }
234
- if spec .wait {
235
- args = append (args , "--wait" )
236
- }
237
- return args
238
-
284
+ return "ptvsd"
239
285
case debugpy :
240
- args := []string {"-mdebugpy" }
286
+ return "debugpy"
287
+ }
288
+ logrus .Fatalf ("invalid debugger type: %q" , spec .debugger )
289
+ return ""
290
+ }
241
291
242
- if spec . host == "" {
243
- args = append ( args , "--listen" , strconv . FormatInt ( int64 ( spec .port ), 10 ))
244
- } else {
245
- args = append ( args , "--listen" , fmt . Sprintf ( "%s:%d" , spec . host , spec . port ))
246
- }
247
- if spec . wait {
248
- args = append ( args , "--wait-for-client" )
249
- }
250
- return args
292
+ func ( spec pythonSpec ) protocol () string {
293
+ switch spec .debugger {
294
+ case pydevd :
295
+ return pydevdProtocol
296
+ case debugpy , ptvsd :
297
+ return dapProtocol
298
+ default :
299
+ logrus . Fatalf ( "invalid debugger type: %q" , spec . debugger )
300
+ return dapProtocol
251
301
}
252
- return nil
253
302
}
0 commit comments