Skip to content

Commit

Permalink
Add test for python executable search logic.
Browse files Browse the repository at this point in the history
Break out the search logic into a separate function and add a regression
test.

References: #668
PR-URL: #756
Reviewed-By: Sakthipriyan Vairamani <thechargingvolcano@gmail.com>
  • Loading branch information
bnoordhuis committed Nov 24, 2015
1 parent 0e2dfda commit 817ed9b
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 90 deletions.
192 changes: 102 additions & 90 deletions lib/configure.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
module.exports = exports = configure
module.exports.test = { findPython: findPython }

/**
* Module dependencies.
Expand Down Expand Up @@ -32,97 +33,14 @@ function configure (gyp, argv, callback) {
, nodeDir
, release = processRelease(argv, gyp, process.version, process.release)

checkPython()

// Check if Python is in the $PATH
function checkPython () {
log.verbose('check python', 'checking for Python executable "%s" in the PATH', python)
which(python, function (err, execPath) {
if (err) {
log.verbose('`which` failed', python, err)
if (python === 'python2') {
python = 'python'
return checkPython()
}
if (win) {
guessPython()
} else {
failNoPython()
}
} else {
log.verbose('`which` succeeded', python, execPath)
checkPythonVersion()
}
})
}

// Called on Windows when "python" isn't available in the current $PATH.
// We're gonna check if "%SystemDrive%\python27\python.exe" exists.
function guessPython () {
log.verbose('could not find "' + python + '". guessing location')
var rootDir = process.env.SystemDrive || 'C:\\'
if (rootDir[rootDir.length - 1] !== '\\') {
rootDir += '\\'
findPython(python, function (err, found) {
if (err) {
callback(err)
} else {
python = found
getNodeDir()
}
var pythonPath = path.resolve(rootDir, 'Python27', 'python.exe')
log.verbose('ensuring that file exists:', pythonPath)
fs.stat(pythonPath, function (err, stat) {
if (err) {
if (err.code == 'ENOENT') {
failNoPython()
} else {
callback(err)
}
return
}
python = pythonPath
checkPythonVersion()
})
}

function checkPythonVersion () {
var env = extend({}, process.env)
env.TERM = 'dumb'

execFile(python, ['-c', 'import platform; print(platform.python_version());'], { env: env }, function (err, stdout) {
if (err) {
return callback(err)
}
log.verbose('check python version', '`%s -c "import platform; print(platform.python_version());"` returned: %j', python, stdout)
var version = stdout.trim()
if (~version.indexOf('+')) {
log.silly('stripping "+" sign(s) from version')
version = version.replace(/\+/g, '')
}
if (~version.indexOf('rc')) {
log.silly('stripping "rc" identifier from version')
version = version.replace(/rc(.*)$/ig, '')
}
var range = semver.Range('>=2.5.0 <3.0.0')
var valid = false
try {
valid = range.test(version)
} catch (e) {
log.silly('range.test() error', e)
}
if (valid) {
getNodeDir()
} else {
failPythonVersion(version)
}
})
}

function failNoPython () {
callback(new Error('Can\'t find Python executable "' + python +
'", you can set the PYTHON env variable.'))
}

function failPythonVersion (badVersion) {
callback(new Error('Python executable "' + python +
'" is v' + badVersion + ', which is not supported by gyp.\n' +
'You can pass the --python switch to point to Python >= v2.5.0 & < 3.0.0.'))
}
})

function getNodeDir () {

Expand Down Expand Up @@ -392,3 +310,97 @@ function configure (gyp, argv, callback) {
}

}

function findPython (python, callback) {
checkPython()

// Check if Python is in the $PATH
function checkPython () {
log.verbose('check python', 'checking for Python executable "%s" in the PATH', python)
which(python, function (err, execPath) {
if (err) {
log.verbose('`which` failed', python, err)
if (python === 'python2') {
python = 'python'
return checkPython()
}
if (win) {
guessPython()
} else {
failNoPython()
}
} else {
log.verbose('`which` succeeded', python, execPath)
checkPythonVersion()
}
})
}

// Called on Windows when "python" isn't available in the current $PATH.
// We're gonna check if "%SystemDrive%\python27\python.exe" exists.
function guessPython () {
log.verbose('could not find "' + python + '". guessing location')
var rootDir = process.env.SystemDrive || 'C:\\'
if (rootDir[rootDir.length - 1] !== '\\') {
rootDir += '\\'
}
var pythonPath = path.resolve(rootDir, 'Python27', 'python.exe')
log.verbose('ensuring that file exists:', pythonPath)
fs.stat(pythonPath, function (err, stat) {
if (err) {
if (err.code == 'ENOENT') {
failNoPython()
} else {
callback(err)
}
return
}
python = pythonPath
checkPythonVersion()
})
}

function checkPythonVersion () {
var env = extend({}, process.env)
env.TERM = 'dumb'

execFile(python, ['-c', 'import platform; print(platform.python_version());'], { env: env }, function (err, stdout) {
if (err) {
return callback(err)
}
log.verbose('check python version', '`%s -c "import platform; print(platform.python_version());"` returned: %j', python, stdout)
var version = stdout.trim()
if (~version.indexOf('+')) {
log.silly('stripping "+" sign(s) from version')
version = version.replace(/\+/g, '')
}
if (~version.indexOf('rc')) {
log.silly('stripping "rc" identifier from version')
version = version.replace(/rc(.*)$/ig, '')
}
var range = semver.Range('>=2.5.0 <3.0.0')
var valid = false
try {
valid = range.test(version)
} catch (e) {
log.silly('range.test() error', e)
}
if (valid) {
callback(null, python)
} else {
failPythonVersion(version)
}
})
}

function failNoPython () {
callback(new Error('Can\'t find Python executable "' + python +
'", you can set the PYTHON env variable.'))
}

function failPythonVersion (badVersion) {
callback(new Error('Python executable "' + python +
'" is v' + badVersion + ', which is not supported by gyp.\n' +
'You can pass the --python switch to point to Python >= v2.5.0 & < 3.0.0.'))
}
}
20 changes: 20 additions & 0 deletions test/test-find-python.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
'use strict'

var test = require('tape')
var configure = require('../lib/configure')
var execFile = require('child_process').execFile

test('find python executable', function (t) {
t.plan(4)

configure.test.findPython('python', function (err, found) {
t.strictEqual(err, null)
var proc = execFile(found, ['-V'], function (err, stdout, stderr) {
t.strictEqual(err, null)
t.strictEqual(stdout, '')
t.ok(/Python 2/.test(stderr))
})
proc.stdout.setEncoding('utf-8')
proc.stderr.setEncoding('utf-8')
})
})

0 comments on commit 817ed9b

Please sign in to comment.