@@ -34,6 +34,12 @@ export class ConnectionClosedError extends Error {
34
34
super ( ) ;
35
35
}
36
36
}
37
+
38
+ export class DaemonError extends Error {
39
+ constructor ( public readonly message : string ) {
40
+ super ( ) ;
41
+ }
42
+ }
37
43
export class PythonDaemonExecutionService implements IPythonDaemonExecutionService {
38
44
private connectionClosedMessage : string = '' ;
39
45
private outputObservale = new Subject < Output < string > > ( ) ;
@@ -66,8 +72,8 @@ export class PythonDaemonExecutionService implements IPythonDaemonExecutionServi
66
72
this . disposables . forEach ( item => item . dispose ( ) ) ;
67
73
}
68
74
public async getInterpreterInformation ( ) : Promise < InterpreterInfomation | undefined > {
69
- this . throwIfRPCConnectionIsDead ( ) ;
70
75
try {
76
+ this . throwIfRPCConnectionIsDead ( ) ;
71
77
type InterpreterInfoResponse = ErrorResponse & { versionInfo : PythonVersionInfo ; sysPrefix : string ; sysVersion : string ; is64Bit : boolean } ;
72
78
const request = new RequestType0 < InterpreterInfoResponse , void , void > ( 'get_interpreter_information' ) ;
73
79
const response = await this . sendRequestWithoutArgs ( request ) ;
@@ -79,69 +85,104 @@ export class PythonDaemonExecutionService implements IPythonDaemonExecutionServi
79
85
sysVersion : response . sysVersion ,
80
86
sysPrefix : response . sysPrefix
81
87
} ;
82
- } catch {
88
+ } catch ( ex ) {
89
+ traceWarning ( 'Falling back to Python Execution Service due to failure in daemon' , ex ) ;
83
90
return this . pythonExecutionService . getInterpreterInformation ( ) ;
84
91
}
85
92
}
86
93
public async getExecutablePath ( ) : Promise < string > {
87
- this . throwIfRPCConnectionIsDead ( ) ;
88
94
try {
95
+ this . throwIfRPCConnectionIsDead ( ) ;
89
96
type ExecutablePathResponse = ErrorResponse & { path : string } ;
90
97
const request = new RequestType0 < ExecutablePathResponse , void , void > ( 'get_executable' ) ;
91
98
const response = await this . sendRequestWithoutArgs ( request ) ;
92
99
if ( response . error ) {
93
- throw new Error ( response . error ) ;
100
+ throw new DaemonError ( response . error ) ;
94
101
}
95
102
return response . path ;
96
- } catch {
103
+ } catch ( ex ) {
104
+ traceWarning ( 'Falling back to Python Execution Service due to failure in daemon' , ex ) ;
97
105
return this . pythonExecutionService . getExecutablePath ( ) ;
98
106
}
99
107
}
100
108
public getExecutionInfo ( args : string [ ] ) : PythonExecutionInfo {
101
109
return this . pythonExecutionService . getExecutionInfo ( args ) ;
102
110
}
103
111
public async isModuleInstalled ( moduleName : string ) : Promise < boolean > {
104
- this . throwIfRPCConnectionIsDead ( ) ;
105
112
try {
113
+ this . throwIfRPCConnectionIsDead ( ) ;
106
114
type ModuleInstalledResponse = ErrorResponse & { exists : boolean } ;
107
115
const request = new RequestType < { module_name : string } , ModuleInstalledResponse , void , void > ( 'is_module_installed' ) ;
108
116
const response = await this . sendRequest ( request , { module_name : moduleName } ) ;
109
117
if ( response . error ) {
110
- throw new Error ( response . error ) ;
118
+ throw new DaemonError ( response . error ) ;
111
119
}
112
120
return response . exists ;
113
- } catch {
121
+ } catch ( ex ) {
122
+ traceWarning ( 'Falling back to Python Execution Service due to failure in daemon' , ex ) ;
114
123
return this . pythonExecutionService . isModuleInstalled ( moduleName ) ;
115
124
}
116
125
}
117
126
public execObservable ( args : string [ ] , options : SpawnOptions ) : ObservableExecutionResult < string > {
118
- this . throwIfRPCConnectionIsDead ( ) ;
119
- if ( this . canExecFileUsingDaemon ( args , options ) ) {
120
- return this . execFileWithDaemonAsObservable ( args [ 0 ] , args . slice ( 1 ) , options ) ;
127
+ if ( this . isAlive && this . canExecFileUsingDaemon ( args , options ) ) {
128
+ try {
129
+ return this . execAsObservable ( { fileName : args [ 0 ] } , args . slice ( 1 ) , options ) ;
130
+ } catch ( ex ) {
131
+ if ( ex instanceof DaemonError || ex instanceof ConnectionClosedError ) {
132
+ traceWarning ( 'Falling back to Python Execution Service due to failure in daemon' , ex ) ;
133
+ return this . pythonExecutionService . execObservable ( args , options ) ;
134
+ } else {
135
+ throw ex ;
136
+ }
137
+ }
121
138
} else {
122
139
return this . pythonExecutionService . execObservable ( args , options ) ;
123
140
}
124
141
}
125
142
public execModuleObservable ( moduleName : string , args : string [ ] , options : SpawnOptions ) : ObservableExecutionResult < string > {
126
- this . throwIfRPCConnectionIsDead ( ) ;
127
- if ( this . canExecModuleUsingDaemon ( moduleName , args , options ) ) {
128
- return this . execModuleWithDaemonAsObservable ( moduleName , args , options ) ;
143
+ if ( this . isAlive && this . canExecModuleUsingDaemon ( moduleName , args , options ) ) {
144
+ try {
145
+ return this . execAsObservable ( { moduleName } , args , options ) ;
146
+ } catch ( ex ) {
147
+ if ( ex instanceof DaemonError || ex instanceof ConnectionClosedError ) {
148
+ traceWarning ( 'Falling back to Python Execution Service due to failure in daemon' , ex ) ;
149
+ return this . pythonExecutionService . execModuleObservable ( moduleName , args , options ) ;
150
+ } else {
151
+ throw ex ;
152
+ }
153
+ }
129
154
} else {
130
155
return this . pythonExecutionService . execModuleObservable ( moduleName , args , options ) ;
131
156
}
132
157
}
133
158
public async exec ( args : string [ ] , options : SpawnOptions ) : Promise < ExecutionResult < string > > {
134
- this . throwIfRPCConnectionIsDead ( ) ;
135
- if ( this . canExecFileUsingDaemon ( args , options ) ) {
136
- return this . execFileWithDaemon ( args [ 0 ] , args . slice ( 1 ) , options ) ;
159
+ if ( this . isAlive && this . canExecFileUsingDaemon ( args , options ) ) {
160
+ try {
161
+ return await this . execFileWithDaemon ( args [ 0 ] , args . slice ( 1 ) , options ) ;
162
+ } catch ( ex ) {
163
+ if ( ex instanceof DaemonError || ex instanceof ConnectionClosedError ) {
164
+ traceWarning ( 'Falling back to Python Execution Service due to failure in daemon' , ex ) ;
165
+ return this . pythonExecutionService . exec ( args , options ) ;
166
+ } else {
167
+ throw ex ;
168
+ }
169
+ }
137
170
} else {
138
171
return this . pythonExecutionService . exec ( args , options ) ;
139
172
}
140
173
}
141
174
public async execModule ( moduleName : string , args : string [ ] , options : SpawnOptions ) : Promise < ExecutionResult < string > > {
142
- this . throwIfRPCConnectionIsDead ( ) ;
143
- if ( this . canExecModuleUsingDaemon ( moduleName , args , options ) ) {
144
- return this . execModuleWithDaemon ( moduleName , args , options ) ;
175
+ if ( this . isAlive && this . canExecModuleUsingDaemon ( moduleName , args , options ) ) {
176
+ try {
177
+ return await this . execModuleWithDaemon ( moduleName , args , options ) ;
178
+ } catch ( ex ) {
179
+ if ( ex instanceof DaemonError || ex instanceof ConnectionClosedError ) {
180
+ traceWarning ( 'Falling back to Python Execution Service due to failure in daemon' , ex ) ;
181
+ return this . pythonExecutionService . execModule ( moduleName , args , options ) ;
182
+ } else {
183
+ throw ex ;
184
+ }
185
+ }
145
186
} else {
146
187
return this . pythonExecutionService . execModule ( moduleName , args , options ) ;
147
188
}
@@ -174,7 +215,7 @@ export class PythonDaemonExecutionService implements IPythonDaemonExecutionServi
174
215
*/
175
216
private processResponse ( response : { error ?: string | undefined ; stdout : string ; stderr ?: string } , options : SpawnOptions ) {
176
217
if ( response . error ) {
177
- throw new StdErrError ( `Failed to execute using the daemon, ${ response . error } ` ) ;
218
+ throw new DaemonError ( `Failed to execute using the daemon, ${ response . error } ` ) ;
178
219
}
179
220
// Throw an error if configured to do so if there's any output in stderr.
180
221
if ( response . stderr && options . throwOnStdErr ) {
@@ -193,9 +234,6 @@ export class PythonDaemonExecutionService implements IPythonDaemonExecutionServi
193
234
this . processResponse ( response , options ) ;
194
235
return response ;
195
236
}
196
- private execFileWithDaemonAsObservable ( fileName : string , args : string [ ] , options : SpawnOptions ) : ObservableExecutionResult < string > {
197
- return this . execAsObservable ( { fileName } , args , options ) ;
198
- }
199
237
private async execModuleWithDaemon ( moduleName : string , args : string [ ] , options : SpawnOptions ) : Promise < ExecutionResult < string > > {
200
238
type ExecResponse = ErrorResponse & { stdout : string ; stderr ?: string } ;
201
239
// tslint:disable-next-line: no-any
@@ -204,9 +242,6 @@ export class PythonDaemonExecutionService implements IPythonDaemonExecutionServi
204
242
this . processResponse ( response , options ) ;
205
243
return response ;
206
244
}
207
- private execModuleWithDaemonAsObservable ( moduleName : string , args : string [ ] , options : SpawnOptions ) : ObservableExecutionResult < string > {
208
- return this . execAsObservable ( { moduleName } , args , options ) ;
209
- }
210
245
private execAsObservable ( moduleOrFile : { moduleName : string } | { fileName : string } , args : string [ ] , options : SpawnOptions ) : ObservableExecutionResult < string > {
211
246
const subject = new Subject < Output < string > > ( ) ;
212
247
const start = async ( ) => {
@@ -223,7 +258,7 @@ export class PythonDaemonExecutionService implements IPythonDaemonExecutionServi
223
258
}
224
259
// Might not get a response object back, as its observable.
225
260
if ( response && response . error ) {
226
- throw new StdErrError ( response . error ) ;
261
+ throw new DaemonError ( response . error ) ;
227
262
}
228
263
} ;
229
264
let stdErr = '' ;
@@ -280,21 +315,22 @@ export class PythonDaemonExecutionService implements IPythonDaemonExecutionServi
280
315
this . connection . onNotification ( OuputNotification , output => this . outputObservale . next ( output ) ) ;
281
316
const logNotification = new NotificationType < { level : 'WARN' | 'WARNING' | 'INFO' | 'DEBUG' | 'NOTSET' ; msg : string } , void > ( 'log' ) ;
282
317
this . connection . onNotification ( logNotification , output => {
318
+ const msg = `Python Daemon: ${ output . msg } ` ;
283
319
if ( output . level === 'DEBUG' || output . level === 'NOTSET' ) {
284
- traceVerbose ( output . msg ) ;
320
+ traceVerbose ( msg ) ;
285
321
} else if ( output . level === 'INFO' ) {
286
- traceInfo ( output . msg ) ;
322
+ traceInfo ( msg ) ;
287
323
} else if ( output . level === 'WARN' || output . level === 'WARNING' ) {
288
- traceWarning ( output . msg ) ;
324
+ traceWarning ( msg ) ;
289
325
} else {
290
- traceError ( output . msg ) ;
326
+ traceError ( msg ) ;
291
327
}
292
328
} ) ;
293
329
this . connection . onUnhandledNotification ( traceError ) ;
294
330
}
295
331
private throwIfRPCConnectionIsDead ( ) {
296
- if ( this . connectionClosedMessage ) {
297
- throw new Error ( this . connectionClosedMessage ) ;
332
+ if ( ! this . isAlive ) {
333
+ throw new ConnectionClosedError ( this . connectionClosedMessage ) ;
298
334
}
299
335
}
300
336
}
0 commit comments