Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Try python launcher when stock python is python 3. #992

Merged
merged 2 commits into from
Oct 8, 2016
Merged
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
193 changes: 114 additions & 79 deletions lib/configure.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
module.exports = exports = configure
module.exports.test = { findAccessibleSync: findAccessibleSync,
findPython: findPython }
module.exports.test = {
PythonFinder: PythonFinder,
findAccessibleSync: findAccessibleSync,
findPython: findPython,
}

/**
* Module dependencies.
Expand All @@ -16,8 +19,6 @@ var fs = require('graceful-fs')
, cp = require('child_process')
, extend = require('util')._extend
, processRelease = require('./process-release')
, spawn = cp.spawn
, execFile = cp.execFile
, win = process.platform == 'win32'
, findNodeDirectory = require('./find-node-directory')
, msgFormat = require('util').format
Expand Down Expand Up @@ -336,34 +337,46 @@ function findAccessibleSync (logprefix, dir, candidates) {
return undefined
}

function findPython (python, callback) {
checkPython()
function PythonFinder(python, callback) {
this.callback = callback
this.python = python
}

// 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) {
PythonFinder.prototype = {
checkPythonLauncherDepth: 0,
env: process.env,
execFile: cp.execFile,
log: log,
stat: fs.stat,
which: which,
win: win,

checkPython: function checkPython () {
this.log.verbose('check python',
'checking for Python executable "%s" in the PATH',
this.python)
this.which(this.python, function (err, execPath) {
if (err) {
log.verbose('`which` failed', python, err)
if (python === 'python2') {
python = 'python'
return checkPython()
this.log.verbose('`which` failed', this.python, err)
if (this.python === 'python2') {
this.python = 'python'
return this.checkPython()
}
if (win) {
checkPythonLauncher()
if (this.win) {
this.checkPythonLauncher()
} else {
failNoPython()
this.failNoPython()
}
} else {
log.verbose('`which` succeeded', python, execPath)
// Found the `python` exceutable, and from now on we use it explicitly.
this.log.verbose('`which` succeeded', this.python, execPath)
// Found the `python` executable, and from now on we use it explicitly.
// This solves #667 and #750 (`execFile` won't run batch files
// (*.cmd, and *.bat))
python = execPath
checkPythonVersion()
this.python = execPath
this.checkPythonVersion()
}
})
}
}.bind(this))
},

// Distributions of Python on Windows by default install with the "py.exe"
// Python launcher which is more likely to exist than the Python executable
Expand All @@ -373,88 +386,110 @@ function findPython (python, callback) {
// the first command line argument. Since "py.exe -2" would be an invalid
// executable for "execFile", we have to use the launcher to figure out
// where the actual "python.exe" executable is located.
function checkPythonLauncher () {
log.verbose('could not find "' + python + '". checking python launcher')
var env = extend({}, process.env)
checkPythonLauncher: function checkPythonLauncher () {
this.checkPythonLauncherDepth += 1

this.log.verbose(
'could not find "' + this.python + '". checking python launcher')
var env = extend({}, this.env)
env.TERM = 'dumb'

var launcherArgs = ['-2', '-c', 'import sys; print sys.executable']
execFile('py.exe', launcherArgs, { env: env }, function (err, stdout) {
if (err) {
guessPython()
return
}
python = stdout.trim()
log.verbose('check python launcher', 'python executable found: %j', python)
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) {
this.execFile('py.exe', launcherArgs, { env: env }, function (err, stdout) {
if (err) {
if (err.code == 'ENOENT') {
failNoPython()
} else {
callback(err)
}
return
this.guessPython()
} else {
this.python = stdout.trim()
this.log.verbose('check python launcher',
'python executable found: %j',
this.python)
this.checkPythonVersion()
}
python = pythonPath
checkPythonVersion()
})
}
this.checkPythonLauncherDepth -= 1
}.bind(this))
},

function checkPythonVersion () {
var env = extend({}, process.env)
checkPythonVersion: function checkPythonVersion () {
var args = ['-c', 'import platform; print(platform.python_version());']
var env = extend({}, this.env)
env.TERM = 'dumb'

execFile(python, ['-c', 'import platform; print(platform.python_version());'], { env: env }, function (err, stdout) {
this.execFile(this.python, args, { env: env }, function (err, stdout) {
if (err) {
return callback(err)
return this.callback(err)
}
log.verbose('check python version', '`%s -c "import platform; print(platform.python_version());"` returned: %j', python, stdout)
this.log.verbose('check python version',
'`%s -c "' + args[1] + '"` returned: %j',
this.python, stdout)
var version = stdout.trim()
if (~version.indexOf('+')) {
log.silly('stripping "+" sign(s) from version')
this.log.silly('stripping "+" sign(s) from version')
version = version.replace(/\+/g, '')
}
if (~version.indexOf('rc')) {
log.silly('stripping "rc" identifier from version')
this.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)
this.log.silly('range.test() error', e)
}
if (valid) {
callback(null, python)
this.callback(null, this.python)
} else if (this.win && this.checkPythonLauncherDepth === 0) {
this.checkPythonLauncher()
} else {
failPythonVersion(version)
this.failPythonVersion(version)
}
})
}
}.bind(this))
},

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

failPythonVersion: function failPythonVersion (badVersion) {
var errmsg =
'Python executable "' + this.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.'
this.callback(new Error(errmsg))
},

function failNoPython () {
callback(new Error('Can\'t find Python executable "' + python +
'", you can set the PYTHON env variable.'))
}
// Called on Windows when "python" isn't available in the current $PATH.
// We are going to check if "%SystemDrive%\python27\python.exe" exists.
guessPython: function guessPython () {
this.log.verbose('could not find "' + this.python + '". guessing location')
var rootDir = this.env.SystemDrive || 'C:\\'
if (rootDir[rootDir.length - 1] !== '\\') {
rootDir += '\\'
}
var resolve = path.win32 && path.win32.resolve || path.resolve
var pythonPath = resolve(rootDir, 'Python27', 'python.exe')
this.log.verbose('ensuring that file exists:', pythonPath)
this.stat(pythonPath, function (err, stat) {
if (err) {
if (err.code == 'ENOENT') {
this.failNoPython()
} else {
this.callback(err)
}
return
}
this.python = pythonPath
this.checkPythonVersion()
}.bind(this))
},
}

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 findPython (python, callback) {
var finder = new PythonFinder(python, callback)
finder.checkPython()
}
Loading