Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,48 @@ export class PythonShell extends EventEmitter {
});
}

/**
* checks syntax via stdin without writing to a temp file
* @param code The Python code to check
* @returns rejects promise w/ stderr if syntax failure
*/
static checkSyntaxStdin(
code: string,
): Promise<{ stdout: string; stderr: string }> {
return new Promise((resolve, reject) => {
const pythonPath = this.getPythonPath();
const child = spawn(pythonPath, [
'-c',
'import sys; compile(sys.stdin.read(), "<stdin>", "exec")',
]);

const outputBuffers = { stdout: '', stderr: '' };

child.stdout.on('data', (data) => {
outputBuffers.stdout += data.toString();
});

child.stderr.on('data', (data) => {
outputBuffers.stderr += data.toString();
});

child.on('close', (exitCode) => {
if (exitCode === 0) {
resolve(outputBuffers);
return;
}
reject(outputBuffers.stderr || `Process exited with code ${exitCode}`);
});

child.on('error', (err) => {
reject(err);
});

child.stdin.write(code);
child.stdin.end();
});
}

static getPythonPath() {
return this.defaultOptions.pythonPath
? this.defaultOptions.pythonPath
Expand Down
63 changes: 62 additions & 1 deletion test/test-python-shell.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import * as should from 'should';
import { PythonShell } from '..';
import { sep, join } from 'path';
import { EOL as newline } from 'os';
import { EOL as newline, tmpdir } from 'os';
import { chdir, cwd } from 'process';
import { readdirSync } from 'fs';

describe('PythonShell', function () {
const pythonFolder = 'test/python';
Expand Down Expand Up @@ -98,6 +99,66 @@ describe('PythonShell', function () {
});
});

describe('#checkSyntaxStdin(code:string)', function () {
it('should not create temp files', function (done) {
const tmpFiles = readdirSync(tmpdir()).filter((f) =>
f.startsWith('pythonShellSyntaxCheck'),
);
const beforeCount = tmpFiles.length;

PythonShell.checkSyntaxStdin('x=1').then(() => {
const afterFiles = readdirSync(tmpdir()).filter((f) =>
f.startsWith('pythonShellSyntaxCheck'),
);
afterFiles.length.should.equal(beforeCount);
done();
});
});
it('should check syntax via stdin', function (done) {
PythonShell.checkSyntaxStdin('x=1').then(() => {
done();
});
});

it('should check multiline code via stdin', function (done) {
const code = `def hello():
print("world")
hello()`;
PythonShell.checkSyntaxStdin(code).then(() => {
done();
});
});

it('should invalidate bad syntax via stdin', function (done) {
PythonShell.checkSyntaxStdin('x=').catch(() => {
done();
});
});

it('should invalidate syntax error in multiline code via stdin', function (done) {
const code = `def hello()
print("world")`;
PythonShell.checkSyntaxStdin(code).catch(() => {
done();
});
});

it('should use pythonOptions from defaultOptions', function (done) {
const originalOptions = PythonShell.defaultOptions;
PythonShell.defaultOptions = { pythonOptions: ['-B'] };

PythonShell.checkSyntaxStdin('x=1')
.then(() => {
PythonShell.defaultOptions = originalOptions;
done();
})
.catch((err) => {
PythonShell.defaultOptions = originalOptions;
done(err);
});
});
});

// #158 these tests are failing on appveyor windows node 8/10 python 2/3
// but they work locally on my windows machine .....
// these methods are not that important so just commenting out tests untill someone fixes them
Expand Down