Skip to content

Commit 4c9e327

Browse files
authored
Merge pull request #269 from NoamGaash/feat/promisify
feat: promisify the "run" command
2 parents c648860 + 21571c6 commit 4c9e327

File tree

3 files changed

+60
-14
lines changed

3 files changed

+60
-14
lines changed

index.ts

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -309,10 +309,32 @@ export class PythonShell extends EventEmitter {
309309
* Runs a Python script and returns collected messages
310310
* @param {string} scriptPath The path to the script to execute
311311
* @param {Options} options The execution options
312-
* @param {Function} callback The callback function to invoke with the script results
313-
* @return {PythonShell} The PythonShell instance
312+
* @param {Function} (deprecated argument) callback The callback function to invoke with the script results
313+
* @return {Promise<string[]> | PythonShell} the output from the python script
314314
*/
315-
static run(scriptPath: string, options?: Options, callback?: (err?: PythonShellError, output?: any[]) => any) {
315+
static run(scriptPath: string, options?: Options, callback?: (err?: PythonShellError, output?: any[]) => any) {
316+
317+
if(callback) {
318+
console.warn('PythonShell.run() callback is deprecated. Use PythonShell.run() promise instead.')
319+
320+
return this.runLegacy(scriptPath, options, callback);
321+
}
322+
else {
323+
return new Promise((resolve, reject) => {
324+
let pyshell = new PythonShell(scriptPath, options);
325+
let output = [];
326+
327+
pyshell.on('message', function (message) {
328+
output.push(message);
329+
}).end(function (err) {
330+
if(err) reject(err);
331+
else resolve(output);
332+
});
333+
});
334+
}
335+
};
336+
337+
private static runLegacy(scriptPath: string, options?: Options, callback?: (err?: PythonShellError, output?: any[]) => any) {
316338
let pyshell = new PythonShell(scriptPath, options);
317339
let output = [];
318340

@@ -323,14 +345,16 @@ export class PythonShell extends EventEmitter {
323345
});
324346
};
325347

348+
349+
326350
/**
327351
* Runs the inputted string of python code and returns collected messages. DO NOT ALLOW UNTRUSTED USER INPUT HERE!
328352
* @param {string} code The python code to execute
329353
* @param {Options} options The execution options
330354
* @param {Function} callback The callback function to invoke with the script results
331355
* @return {PythonShell} The PythonShell instance
332356
*/
333-
static runString(code: string, options?: Options, callback?: (err: PythonShellError, output?: any[]) => any) {
357+
static runString(code: string, options?: Options, callback?: (err: PythonShellError, output?: any[]) => any) {
334358

335359
// put code in temp file
336360
const randomInt = getRandomInt();

package-lock.json

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/test-python-shell.ts

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,20 @@ describe('PythonShell', function () {
117117
before(() => {
118118
PythonShell.defaultOptions = {};
119119
})
120-
it('should be able to execute a string of python code', function (done) {
121-
PythonShell.runString('print("hello");print("world")', null, function (err, results) {
120+
it('should be able to execute a string of python code using callbacks', function (done) {
121+
let pythonshell = PythonShell.runString('print("hello");print("world")', null, function (err, results) {
122122
if (err) return done(err);
123123
results.should.be.an.Array().and.have.lengthOf(2);
124124
results.should.eql(['hello', 'world']);
125125
done();
126126
});
127+
128+
pythonshell.should.be.an.instanceOf(PythonShell);
129+
});
130+
it('should be able to execute a string of python code using promises', async function () {
131+
let results = await PythonShell.runString('print("hello");print("world")');
132+
results.should.be.an.Array().and.have.lengthOf(2);
133+
results.should.eql(['hello', 'world']);
127134
});
128135
after(() => {
129136
PythonShell.defaultOptions = {
@@ -134,7 +141,7 @@ describe('PythonShell', function () {
134141
});
135142

136143
describe('#run(script, options)', function () {
137-
it('should run the script and return output data', function (done) {
144+
it('should run the script and return output data using callbacks', function (done) {
138145
PythonShell.run('echo_args.py', {
139146
args: ['hello', 'world']
140147
}, function (err, results) {
@@ -144,13 +151,29 @@ describe('PythonShell', function () {
144151
done();
145152
});
146153
});
154+
it('should run the script and return output data using promise', async function () {
155+
let results = await PythonShell.run('echo_args.py', {
156+
args: ['hello', 'world']
157+
});
158+
results.should.be.an.Array().and.have.lengthOf(2);
159+
results.should.eql(['hello', 'world']);
160+
});
147161
it('should try to run the script and fail appropriately', function (done) {
148162
PythonShell.run('unknown_script.py', null, function (err, results) {
149163
err.should.be.an.Error;
150164
err.exitCode.should.be.exactly(2);
151165
done();
152166
});
153167
});
168+
it('should try to run the script and fail appropriately', async function () {
169+
try {
170+
let results = await PythonShell.run('unknown_script.py');
171+
throw new Error(`should not get here because the script should fail` + results);
172+
} catch (err) {
173+
err.should.be.an.Error;
174+
err.exitCode.should.be.exactly(2);
175+
}
176+
});
154177
it('should include both output and error', function (done) {
155178
PythonShell.run('echo_hi_then_error.py', null, function (err, results) {
156179
err.should.be.an.Error;
@@ -250,8 +273,8 @@ describe('PythonShell', function () {
250273
};
251274
})
252275

253-
it('should run PythonShell normally without access to std streams', function (done) {
254-
var pyshell = PythonShell.run('exit-code.py', {
276+
it('should run PythonShell normally without access to std streams', async function () {
277+
var pyshell = await PythonShell.run('exit-code.py', {
255278
// 3 different ways of assigning values to the std streams in child_process.spawn()
256279
// * ignore - pipe to /dev/null
257280
// * inherit - inherit fd from parent process;
@@ -261,12 +284,10 @@ describe('PythonShell', function () {
261284
// although the user shouldn't be doing this. We are just testing for
262285
// increased code coverage
263286
args: "0"
264-
}, done);
287+
});
265288

266-
should(pyshell.stdin).be.eql(null);
267-
should(pyshell.stdout).be.eql(null);
268-
should(pyshell.stderr).be.eql(null);
269-
should.throws(() => { pyshell.send("asd") });
289+
should(pyshell).be.eql([]);
290+
270291
});
271292
});
272293

0 commit comments

Comments
 (0)