diff --git a/index.ts b/index.ts index 9569354..abbcad3 100644 --- a/index.ts +++ b/index.ts @@ -3,8 +3,8 @@ import { ChildProcess, spawn, SpawnOptions, - exec, - execSync, + execFile, + execFileSync, } from 'child_process'; import { EOL as newline, tmpdir } from 'os'; import { join, sep } from 'path'; @@ -42,7 +42,7 @@ function getRandomInt() { return Math.floor(Math.random() * 10000000000); } -const execPromise = promisify(exec); +const execFilePromise = promisify(execFile); export interface Options extends SpawnOptions { /** @@ -334,8 +334,7 @@ export class PythonShell extends EventEmitter { */ static async checkSyntaxFile(filePath: string) { const pythonPath = this.getPythonPath(); - let compileCommand = `${pythonPath} -m py_compile ${filePath}`; - return execPromise(compileCommand); + return execFilePromise(pythonPath, ['-m', 'py_compile', filePath]); } /** @@ -379,12 +378,12 @@ export class PythonShell extends EventEmitter { static getVersion(pythonPath?: string) { if (!pythonPath) pythonPath = this.getPythonPath(); - return execPromise(pythonPath + ' --version'); + return execFilePromise(pythonPath, ['--version']); } static getVersionSync(pythonPath?: string) { if (!pythonPath) pythonPath = this.getPythonPath(); - return execSync(pythonPath + ' --version').toString(); + return execFileSync(pythonPath, ['--version']).toString(); } /** diff --git a/test/test-python-shell.ts b/test/test-python-shell.ts index 3d5de81..d73fd4f 100644 --- a/test/test-python-shell.ts +++ b/test/test-python-shell.ts @@ -1,8 +1,16 @@ 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 { + chmodSync, + copyFileSync, + mkdtempSync, + rmSync, + symlinkSync, + writeFileSync, +} from 'fs'; describe('PythonShell', function () { const pythonFolder = 'test/python'; @@ -96,6 +104,64 @@ describe('PythonShell', function () { done(); }); }); + it('should check syntax for files whose path contains spaces', async function () { + const tempDirectory = mkdtempSync(join(tmpdir(), 'python shell syntax ')); + const filePath = join(tempDirectory, 'valid script.py'); + + try { + writeFileSync(filePath, 'x = 1\n'); + const result = await PythonShell.checkSyntaxFile(filePath); + result.stderr.should.eql(''); + } finally { + rmSync(tempDirectory, { recursive: true, force: true }); + } + }); + }); + + describe('#getVersion(pythonPath)', function () { + function createCurrentExecutableAtSpacedPath() { + const tempDirectory = mkdtempSync( + join(tmpdir(), 'python shell version '), + ); + const executablePath = join( + tempDirectory, + process.platform === 'win32' ? 'fake python.exe' : 'fake python', + ); + + try { + symlinkSync(process.execPath, executablePath); + } catch (_) { + copyFileSync(process.execPath, executablePath); + if (process.platform !== 'win32') chmodSync(executablePath, 0o755); + } + + return { tempDirectory, executablePath }; + } + + it('should get the version when the executable path contains spaces', async function () { + const { tempDirectory, executablePath } = + createCurrentExecutableAtSpacedPath(); + + try { + const result = await PythonShell.getVersion(executablePath); + result.stdout.trim().should.eql(process.version); + } finally { + rmSync(tempDirectory, { recursive: true, force: true }); + } + }); + + it('should get the version synchronously when the executable path contains spaces', function () { + const { tempDirectory, executablePath } = + createCurrentExecutableAtSpacedPath(); + + try { + PythonShell.getVersionSync(executablePath) + .trim() + .should.eql(process.version); + } finally { + rmSync(tempDirectory, { recursive: true, force: true }); + } + }); }); // #158 these tests are failing on appveyor windows node 8/10 python 2/3