Skip to content

Commit 96dfae8

Browse files
committed
fix #118
1 parent 5f526b7 commit 96dfae8

File tree

3 files changed

+45
-26
lines changed

3 files changed

+45
-26
lines changed

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,10 +323,19 @@ shell.on('stderr', function (stderr) {
323323

324324
Fires when the process has been terminated, with an error or not.
325325

326-
#### event: `error`
326+
#### event: `pythonError`
327327

328328
Fires when the process terminates with a non-zero exit code.
329329

330+
#### event: `error`
331+
332+
Fires when:
333+
* The process could not be spawned, or
334+
* The process could not be killed, or
335+
* Sending a message to the child process failed.
336+
337+
If the process could not be spawned please double-check that python can be launched from the terminal.
338+
330339
## Used By:
331340

332341
Python-Shell is used by [arepl-vscode](https://github.com/almenon/arepl-vscode), [gitinspector](https://github.com/ejwa/gitinspector), [pyspreadsheet](https://github.com/extrabacon/pyspreadsheet), [AtlantOS Ocean Data QC](https://github.com/ocean-data-qc/ocean-data-qc) and more!

index.ts

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export interface Options extends SpawnOptions {
6262
args?: string[]
6363
}
6464

65-
export class PythonShellError extends Error {
65+
export class PythonError extends Error {
6666
traceback: string | Buffer;
6767
exitCode?: number;
6868
}
@@ -90,7 +90,7 @@ export class PythonShell extends EventEmitter {
9090
private stderrHasEnded: boolean;
9191
private stdoutHasEnded: boolean;
9292
private _remaining: string
93-
private _endCallback: (err: PythonShellError, exitCode: number, exitSignal: string) => any
93+
private _endCallback: (err: PythonError, exitCode: number, exitSignal: string) => any
9494

9595
// starting 2020 python2 is deprecated so we choose 3 as default
9696
static defaultPythonPath = process.platform != "win32" ? "python3" : "python";
@@ -178,6 +178,9 @@ export class PythonShell extends EventEmitter {
178178
self.stdoutHasEnded = true;
179179
}
180180

181+
this.childProcess.on('error', function(err: NodeJS.ErrnoException){
182+
self.emit('error', err);
183+
})
181184
this.childProcess.on('exit', function (code, signal) {
182185
self.exitCode = code;
183186
self.exitSignal = signal;
@@ -187,23 +190,23 @@ export class PythonShell extends EventEmitter {
187190
function terminateIfNeeded() {
188191
if (!self.stderrHasEnded || !self.stdoutHasEnded || (self.exitCode == null && self.exitSignal == null)) return;
189192

190-
let err: PythonShellError;
193+
let err: PythonError;
191194
if (self.exitCode && self.exitCode !== 0) {
192195
if (errorData) {
193196
err = self.parseError(errorData);
194197
} else {
195-
err = new PythonShellError('process exited with code ' + self.exitCode);
198+
err = new PythonError('process exited with code ' + self.exitCode);
196199
}
197-
err = <PythonShellError>extend(err, {
200+
err = <PythonError>extend(err, {
198201
executable: pythonPath,
199202
options: pythonOptions.length ? pythonOptions : null,
200203
script: self.scriptPath,
201204
args: scriptArgs.length ? scriptArgs : null,
202205
exitCode: self.exitCode
203206
});
204207
// do not emit error if only a callback is used
205-
if (self.listeners('error').length || !self._endCallback) {
206-
self.emit('error', err);
208+
if (self.listeners('pythonError').length || !self._endCallback) {
209+
self.emit('pythonError', err);
207210
}
208211
}
209212

@@ -270,7 +273,7 @@ export class PythonShell extends EventEmitter {
270273
* @param {Function} callback The callback function to invoke with the script results
271274
* @return {PythonShell} The PythonShell instance
272275
*/
273-
static run(scriptPath: string, options?: Options, callback?: (err?: PythonShellError, output?: any[]) => any) {
276+
static run(scriptPath: string, options?: Options, callback?: (err?: PythonError, output?: any[]) => any) {
274277
let pyshell = new PythonShell(scriptPath, options);
275278
let output = [];
276279

@@ -288,7 +291,7 @@ export class PythonShell extends EventEmitter {
288291
* @param {Function} callback The callback function to invoke with the script results
289292
* @return {PythonShell} The PythonShell instance
290293
*/
291-
static runString(code: string, options?: Options, callback?: (err: PythonShellError, output?: any[]) => any) {
294+
static runString(code: string, options?: Options, callback?: (err: PythonError, output?: any[]) => any) {
292295

293296
// put code in temp file
294297
const randomInt = getRandomInt();
@@ -315,20 +318,20 @@ export class PythonShell extends EventEmitter {
315318
*/
316319
private parseError(data: string | Buffer) {
317320
let text = '' + data;
318-
let error: PythonShellError;
321+
let error: PythonError;
319322

320323
if (/^Traceback/.test(text)) {
321324
// traceback data is available
322325
let lines = text.trim().split(newline);
323326
let exception = lines.pop();
324-
error = new PythonShellError(exception);
327+
error = new PythonError(exception);
325328
error.traceback = data;
326329
// extend stack trace
327330
error.stack += newline + ' ----- Python Traceback -----' + newline + ' ';
328331
error.stack += lines.slice(1).join(newline + ' ');
329332
} else {
330333
// otherwise, create a simpler error with stderr contents
331-
error = new PythonShellError(text);
334+
error = new PythonError(text);
332335
}
333336

334337
return error;
@@ -396,7 +399,7 @@ export class PythonShell extends EventEmitter {
396399
* this should cause the process to finish its work and close.
397400
* @returns {PythonShell} The same instance for chaining calls
398401
*/
399-
end(callback: (err: PythonShellError, exitCode: number, exitSignal: string) => any) {
402+
end(callback: (err: PythonError, exitCode: number, exitSignal: string) => any) {
400403
if (this.childProcess.stdin) {
401404
this.childProcess.stdin.end();
402405
}
@@ -454,10 +457,17 @@ export interface PythonShell {
454457
prependListener(event: "close", listener: () => void): this;
455458
prependOnceListener(event: "close", listener: () => void): this;
456459

457-
addListener(event: "error", listener: (error: PythonShellError) => void): this;
458-
emit(event: "error", error: PythonShellError): boolean;
459-
on(event: "error", listener: (error: PythonShellError) => void): this;
460-
once(event: "error", listener: (error: PythonShellError) => void): this;
461-
prependListener(event: "error", listener: (error: PythonShellError) => void): this;
462-
prependOnceListener(event: "error", listener: (error: PythonShellError) => void): this;
460+
addListener(event: "error", listener: (error: NodeJS.ErrnoException) => void): this;
461+
emit(event: "error", error: NodeJS.ErrnoException): boolean;
462+
on(event: "error", listener: (error: NodeJS.ErrnoException) => void): this;
463+
once(event: "error", listener: (error: NodeJS.ErrnoException) => void): this;
464+
prependListener(event: "error", listener: (error: NodeJS.ErrnoException) => void): this;
465+
prependOnceListener(event: "error", listener: (error: NodeJS.ErrnoException) => void): this;
466+
467+
addListener(event: "pythonError", listener: (error: PythonError) => void): this;
468+
emit(event: "pythonError", error: PythonError): boolean;
469+
on(event: "pythonError", listener: (error: PythonError) => void): this;
470+
once(event: "pythonError", listener: (error: PythonError) => void): this;
471+
prependListener(event: "pythonError", listener: (error: PythonError) => void): this;
472+
prependOnceListener(event: "pythonError", listener: (error: PythonError) => void): this;
463473
}

test/test-python-shell.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ describe('PythonShell', function () {
5959
let pyshell = new PythonShell('exit-code.py', {
6060
pythonPath: 'foeisjofseij'
6161
}, );
62-
pyshell.on('error', (err: NodeJS.ErrnoException)=>{
62+
pyshell.on('error', (err)=>{
6363
err.code.should.eql('ENOENT')
6464
done()
6565
})
@@ -450,7 +450,7 @@ describe('PythonShell', function () {
450450
let pyshell = new PythonShell('exit-code.py', {
451451
args: ['3']
452452
});
453-
pyshell.on('error', function (err) {
453+
pyshell.on('pythonError', function (err) {
454454
err.should.have.properties({
455455
message: 'process exited with code 3',
456456
exitCode: 3
@@ -460,7 +460,7 @@ describe('PythonShell', function () {
460460
});
461461
it('should emit error when the program exits because of an unhandled exception', function (done) {
462462
let pyshell = new PythonShell('error.py');
463-
pyshell.on('error', function (err) {
463+
pyshell.on('pythonError', function (err) {
464464
err.message.should.be.equalOneOf('ZeroDivisionError: integer division or modulo by zero','ZeroDivisionError: division by zero');
465465
err.should.have.property('traceback');
466466
err.traceback.should.containEql('Traceback (most recent call last)');
@@ -469,7 +469,7 @@ describe('PythonShell', function () {
469469
});
470470
it('should NOT emit error when logging is written to stderr', function (done) {
471471
let pyshell = new PythonShell('stderrLogging.py');
472-
pyshell.on('error', function (err) {
472+
pyshell.on('pythonError', function (err) {
473473
done(new Error("an error should not have been raised"));
474474
});
475475
pyshell.on('close', function(){
@@ -483,14 +483,14 @@ describe('PythonShell', function () {
483483
let pyshell = new PythonShell('exit-code.py', {
484484
args: ['1']
485485
});
486-
pyshell.on('error', function (err) {
486+
pyshell.on('pythonError', function (err) {
487487
err.should.have.properties(['exitCode', 'script', 'options', 'args']);
488488
done();
489489
});
490490
});
491491
it('should extend err.stack with traceback', function (done) {
492492
let pyshell = new PythonShell('error.py');
493-
pyshell.on('error', function (err) {
493+
pyshell.on('pythonError', function (err) {
494494
err.stack.should.containEql('----- Python Traceback -----');
495495
err.stack.should.containEql('File "test' + sep + 'python' + sep + 'error.py", line 4');
496496
err.stack.should.containEql('File "test' + sep + 'python' + sep + 'error.py", line 6');

0 commit comments

Comments
 (0)