@@ -34,6 +34,12 @@ export class ConnectionClosedError extends Error {
3434 super ( ) ;
3535 }
3636}
37+
38+ export class DaemonError extends Error {
39+ constructor ( public readonly message : string ) {
40+ super ( ) ;
41+ }
42+ }
3743export class PythonDaemonExecutionService implements IPythonDaemonExecutionService {
3844 private connectionClosedMessage : string = '' ;
3945 private outputObservale = new Subject < Output < string > > ( ) ;
@@ -66,8 +72,8 @@ export class PythonDaemonExecutionService implements IPythonDaemonExecutionServi
6672 this . disposables . forEach ( item => item . dispose ( ) ) ;
6773 }
6874 public async getInterpreterInformation ( ) : Promise < InterpreterInfomation | undefined > {
69- this . throwIfRPCConnectionIsDead ( ) ;
7075 try {
76+ this . throwIfRPCConnectionIsDead ( ) ;
7177 type InterpreterInfoResponse = ErrorResponse & { versionInfo : PythonVersionInfo ; sysPrefix : string ; sysVersion : string ; is64Bit : boolean } ;
7278 const request = new RequestType0 < InterpreterInfoResponse , void , void > ( 'get_interpreter_information' ) ;
7379 const response = await this . sendRequestWithoutArgs ( request ) ;
@@ -79,69 +85,104 @@ export class PythonDaemonExecutionService implements IPythonDaemonExecutionServi
7985 sysVersion : response . sysVersion ,
8086 sysPrefix : response . sysPrefix
8187 } ;
82- } catch {
88+ } catch ( ex ) {
89+ traceWarning ( 'Falling back to Python Execution Service due to failure in daemon' , ex ) ;
8390 return this . pythonExecutionService . getInterpreterInformation ( ) ;
8491 }
8592 }
8693 public async getExecutablePath ( ) : Promise < string > {
87- this . throwIfRPCConnectionIsDead ( ) ;
8894 try {
95+ this . throwIfRPCConnectionIsDead ( ) ;
8996 type ExecutablePathResponse = ErrorResponse & { path : string } ;
9097 const request = new RequestType0 < ExecutablePathResponse , void , void > ( 'get_executable' ) ;
9198 const response = await this . sendRequestWithoutArgs ( request ) ;
9299 if ( response . error ) {
93- throw new Error ( response . error ) ;
100+ throw new DaemonError ( response . error ) ;
94101 }
95102 return response . path ;
96- } catch {
103+ } catch ( ex ) {
104+ traceWarning ( 'Falling back to Python Execution Service due to failure in daemon' , ex ) ;
97105 return this . pythonExecutionService . getExecutablePath ( ) ;
98106 }
99107 }
100108 public getExecutionInfo ( args : string [ ] ) : PythonExecutionInfo {
101109 return this . pythonExecutionService . getExecutionInfo ( args ) ;
102110 }
103111 public async isModuleInstalled ( moduleName : string ) : Promise < boolean > {
104- this . throwIfRPCConnectionIsDead ( ) ;
105112 try {
113+ this . throwIfRPCConnectionIsDead ( ) ;
106114 type ModuleInstalledResponse = ErrorResponse & { exists : boolean } ;
107115 const request = new RequestType < { module_name : string } , ModuleInstalledResponse , void , void > ( 'is_module_installed' ) ;
108116 const response = await this . sendRequest ( request , { module_name : moduleName } ) ;
109117 if ( response . error ) {
110- throw new Error ( response . error ) ;
118+ throw new DaemonError ( response . error ) ;
111119 }
112120 return response . exists ;
113- } catch {
121+ } catch ( ex ) {
122+ traceWarning ( 'Falling back to Python Execution Service due to failure in daemon' , ex ) ;
114123 return this . pythonExecutionService . isModuleInstalled ( moduleName ) ;
115124 }
116125 }
117126 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+ }
121138 } else {
122139 return this . pythonExecutionService . execObservable ( args , options ) ;
123140 }
124141 }
125142 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+ }
129154 } else {
130155 return this . pythonExecutionService . execModuleObservable ( moduleName , args , options ) ;
131156 }
132157 }
133158 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+ }
137170 } else {
138171 return this . pythonExecutionService . exec ( args , options ) ;
139172 }
140173 }
141174 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+ }
145186 } else {
146187 return this . pythonExecutionService . execModule ( moduleName , args , options ) ;
147188 }
@@ -174,7 +215,7 @@ export class PythonDaemonExecutionService implements IPythonDaemonExecutionServi
174215 */
175216 private processResponse ( response : { error ?: string | undefined ; stdout : string ; stderr ?: string } , options : SpawnOptions ) {
176217 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 } ` ) ;
178219 }
179220 // Throw an error if configured to do so if there's any output in stderr.
180221 if ( response . stderr && options . throwOnStdErr ) {
@@ -193,9 +234,6 @@ export class PythonDaemonExecutionService implements IPythonDaemonExecutionServi
193234 this . processResponse ( response , options ) ;
194235 return response ;
195236 }
196- private execFileWithDaemonAsObservable ( fileName : string , args : string [ ] , options : SpawnOptions ) : ObservableExecutionResult < string > {
197- return this . execAsObservable ( { fileName } , args , options ) ;
198- }
199237 private async execModuleWithDaemon ( moduleName : string , args : string [ ] , options : SpawnOptions ) : Promise < ExecutionResult < string > > {
200238 type ExecResponse = ErrorResponse & { stdout : string ; stderr ?: string } ;
201239 // tslint:disable-next-line: no-any
@@ -204,9 +242,6 @@ export class PythonDaemonExecutionService implements IPythonDaemonExecutionServi
204242 this . processResponse ( response , options ) ;
205243 return response ;
206244 }
207- private execModuleWithDaemonAsObservable ( moduleName : string , args : string [ ] , options : SpawnOptions ) : ObservableExecutionResult < string > {
208- return this . execAsObservable ( { moduleName } , args , options ) ;
209- }
210245 private execAsObservable ( moduleOrFile : { moduleName : string } | { fileName : string } , args : string [ ] , options : SpawnOptions ) : ObservableExecutionResult < string > {
211246 const subject = new Subject < Output < string > > ( ) ;
212247 const start = async ( ) => {
@@ -223,7 +258,7 @@ export class PythonDaemonExecutionService implements IPythonDaemonExecutionServi
223258 }
224259 // Might not get a response object back, as its observable.
225260 if ( response && response . error ) {
226- throw new StdErrError ( response . error ) ;
261+ throw new DaemonError ( response . error ) ;
227262 }
228263 } ;
229264 let stdErr = '' ;
@@ -280,21 +315,22 @@ export class PythonDaemonExecutionService implements IPythonDaemonExecutionServi
280315 this . connection . onNotification ( OuputNotification , output => this . outputObservale . next ( output ) ) ;
281316 const logNotification = new NotificationType < { level : 'WARN' | 'WARNING' | 'INFO' | 'DEBUG' | 'NOTSET' ; msg : string } , void > ( 'log' ) ;
282317 this . connection . onNotification ( logNotification , output => {
318+ const msg = `Python Daemon: ${ output . msg } ` ;
283319 if ( output . level === 'DEBUG' || output . level === 'NOTSET' ) {
284- traceVerbose ( output . msg ) ;
320+ traceVerbose ( msg ) ;
285321 } else if ( output . level === 'INFO' ) {
286- traceInfo ( output . msg ) ;
322+ traceInfo ( msg ) ;
287323 } else if ( output . level === 'WARN' || output . level === 'WARNING' ) {
288- traceWarning ( output . msg ) ;
324+ traceWarning ( msg ) ;
289325 } else {
290- traceError ( output . msg ) ;
326+ traceError ( msg ) ;
291327 }
292328 } ) ;
293329 this . connection . onUnhandledNotification ( traceError ) ;
294330 }
295331 private throwIfRPCConnectionIsDead ( ) {
296- if ( this . connectionClosedMessage ) {
297- throw new Error ( this . connectionClosedMessage ) ;
332+ if ( ! this . isAlive ) {
333+ throw new ConnectionClosedError ( this . connectionClosedMessage ) ;
298334 }
299335 }
300336}
0 commit comments