mirror of
https://github.com/bvanroll/rpiRadio.git
synced 2025-08-30 12:32:47 +00:00
Initial Commit
This commit is contained in:
259
ProjectNow/NodeServer/node_modules/python-shell/index.js
generated
vendored
Executable file
259
ProjectNow/NodeServer/node_modules/python-shell/index.js
generated
vendored
Executable file
@@ -0,0 +1,259 @@
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var path = require('path');
|
||||
var util = require('util');
|
||||
var spawn = require('child_process').spawn;
|
||||
|
||||
function toArray(source) {
|
||||
if (typeof source === 'undefined' || source === null) {
|
||||
return [];
|
||||
} else if (!Array.isArray(source)) {
|
||||
return [source];
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
function extend(obj) {
|
||||
Array.prototype.slice.call(arguments, 1).forEach(function (source) {
|
||||
if (source) {
|
||||
for (var key in source) {
|
||||
obj[key] = source[key];
|
||||
}
|
||||
}
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* An interactive Python shell exchanging data through stdio
|
||||
* @param {string} script The python script to execute
|
||||
* @param {object} [options] The launch options (also passed to child_process.spawn)
|
||||
* @constructor
|
||||
*/
|
||||
var PythonShell = function (script, options) {
|
||||
|
||||
function resolve(type, val) {
|
||||
if (typeof val === 'string') {
|
||||
// use a built-in function using its name
|
||||
return PythonShell[type][val];
|
||||
} else if (typeof val === 'function') {
|
||||
// use a custom function
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
var self = this;
|
||||
var errorData = '';
|
||||
EventEmitter.call(this);
|
||||
|
||||
options = extend({}, PythonShell.defaultOptions, options);
|
||||
var pythonPath = options.pythonPath || 'python';
|
||||
var pythonOptions = toArray(options.pythonOptions);
|
||||
var scriptArgs = toArray(options.args);
|
||||
|
||||
this.script = path.join(options.scriptPath || './', script);
|
||||
this.command = pythonOptions.concat(this.script, scriptArgs);
|
||||
this.mode = options.mode || 'text';
|
||||
this.formatter = resolve('format', options.formatter || this.mode);
|
||||
this.parser = resolve('parse', options.parser || this.mode);
|
||||
this.terminated = false;
|
||||
this.childProcess = spawn(pythonPath, this.command, options);
|
||||
|
||||
['stdout', 'stdin', 'stderr'].forEach(function (name) {
|
||||
self[name] = self.childProcess[name];
|
||||
self.parser && self[name].setEncoding(options.encoding || 'utf8');
|
||||
});
|
||||
|
||||
// parse incoming data on stdout
|
||||
if (this.parser) {
|
||||
this.stdout.on('data', PythonShell.prototype.receive.bind(this));
|
||||
}
|
||||
|
||||
// listen to stderr and emit errors for incoming data
|
||||
this.stderr.on('data', function (data) {
|
||||
errorData += ''+data;
|
||||
});
|
||||
|
||||
this.stderr.on('end', function(){
|
||||
self.stderrHasEnded = true
|
||||
terminateIfNeeded();
|
||||
})
|
||||
|
||||
this.stdout.on('end', function(){
|
||||
self.stdoutHasEnded = true
|
||||
terminateIfNeeded();
|
||||
})
|
||||
|
||||
this.childProcess.on('exit', function (code,signal) {
|
||||
self.exitCode = code;
|
||||
self.exitSignal = signal;
|
||||
terminateIfNeeded();
|
||||
});
|
||||
|
||||
function terminateIfNeeded() {
|
||||
if(!self.stderrHasEnded || !self.stdoutHasEnded || (self.exitCode == null && self.exitSignal == null)) return;
|
||||
|
||||
var err;
|
||||
if (errorData || (self.exitCode && self.exitCode !== 0)) {
|
||||
if (errorData) {
|
||||
err = self.parseError(errorData);
|
||||
} else {
|
||||
err = new Error('process exited with code ' + self.exitCode);
|
||||
}
|
||||
err = extend(err, {
|
||||
executable: pythonPath,
|
||||
options: pythonOptions.length ? pythonOptions : null,
|
||||
script: self.script,
|
||||
args: scriptArgs.length ? scriptArgs : null,
|
||||
exitCode: self.exitCode
|
||||
});
|
||||
// do not emit error if only a callback is used
|
||||
if (self.listeners('error').length || !self._endCallback) {
|
||||
self.emit('error', err);
|
||||
}
|
||||
}
|
||||
|
||||
self.terminated = true;
|
||||
self.emit('close');
|
||||
self._endCallback && self._endCallback(err,self.exitCode,self.exitSignal);
|
||||
};
|
||||
};
|
||||
util.inherits(PythonShell, EventEmitter);
|
||||
|
||||
// allow global overrides for options
|
||||
PythonShell.defaultOptions = {};
|
||||
|
||||
// built-in formatters
|
||||
PythonShell.format = {
|
||||
text: function toText(data) {
|
||||
if (!data) return '';
|
||||
else if (typeof data !== 'string') return data.toString();
|
||||
return data;
|
||||
},
|
||||
json: function toJson(data) {
|
||||
return JSON.stringify(data);
|
||||
}
|
||||
};
|
||||
|
||||
// built-in parsers
|
||||
PythonShell.parse = {
|
||||
text: function asText(data) {
|
||||
return data;
|
||||
},
|
||||
json: function asJson(data) {
|
||||
return JSON.parse(data);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Runs a Python script and returns collected messages
|
||||
* @param {string} script The script to execute
|
||||
* @param {Object} options The execution options
|
||||
* @param {Function} callback The callback function to invoke with the script results
|
||||
* @return {PythonShell} The PythonShell instance
|
||||
*/
|
||||
PythonShell.run = function (script, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
|
||||
var pyshell = new PythonShell(script, options);
|
||||
var output = [];
|
||||
|
||||
return pyshell.on('message', function (message) {
|
||||
output.push(message);
|
||||
}).end(function (err) {
|
||||
if (err) return callback(err);
|
||||
return callback(null, output.length ? output : null);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses an error thrown from the Python process through stderr
|
||||
* @param {string|Buffer} data The stderr contents to parse
|
||||
* @return {Error} The parsed error with extended stack trace when traceback is available
|
||||
*/
|
||||
PythonShell.prototype.parseError = function (data) {
|
||||
var text = ''+data;
|
||||
var error;
|
||||
|
||||
if (/^Traceback/.test(text)) {
|
||||
// traceback data is available
|
||||
var lines = (''+data).trim().split(/\n/g);
|
||||
var exception = lines.pop();
|
||||
error = new Error(exception);
|
||||
error.traceback = data;
|
||||
// extend stack trace
|
||||
error.stack += '\n ----- Python Traceback -----\n ';
|
||||
error.stack += lines.slice(1).join('\n ');
|
||||
} else {
|
||||
// otherwise, create a simpler error with stderr contents
|
||||
error = new Error(text);
|
||||
}
|
||||
|
||||
return error;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sends a message to the Python shell through stdin
|
||||
* Override this method to format data to be sent to the Python process
|
||||
* @param {string|Object} data The message to send
|
||||
* @returns {PythonShell} The same instance for chaining calls
|
||||
*/
|
||||
PythonShell.prototype.send = function (message) {
|
||||
var data = this.formatter ? this.formatter(message) : message;
|
||||
if (this.mode !== 'binary') data += '\n';
|
||||
this.stdin.write(data);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses data received from the Python shell stdout stream and emits "message" events
|
||||
* This method is not used in binary mode
|
||||
* Override this method to parse incoming data from the Python process into messages
|
||||
* @param {string|Buffer} data The data to parse into messages
|
||||
*/
|
||||
PythonShell.prototype.receive = function (data) {
|
||||
var self = this;
|
||||
var parts = (''+data).split(/\n/g);
|
||||
|
||||
if (parts.length === 1) {
|
||||
// an incomplete record, keep buffering
|
||||
this._remaining = (this._remaining || '') + parts[0];
|
||||
return this;
|
||||
}
|
||||
|
||||
var lastLine = parts.pop();
|
||||
// fix the first line with the remaining from the previous iteration of 'receive'
|
||||
parts[0] = (this._remaining || '') + parts[0];
|
||||
// keep the remaining for the next iteration of 'receive'
|
||||
this._remaining = lastLine;
|
||||
|
||||
parts.forEach(function (part) {
|
||||
self.emit('message', self.parser(part));
|
||||
});
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Closes the stdin stream, which should cause the process to finish its work and close
|
||||
* @returns {PythonShell} The same instance for chaining calls
|
||||
*/
|
||||
PythonShell.prototype.end = function (callback) {
|
||||
this.childProcess.stdin.end();
|
||||
this._endCallback = callback;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Closes the stdin stream, which should cause the process to finish its work and close
|
||||
* @returns {PythonShell} The same instance for chaining calls
|
||||
*/
|
||||
PythonShell.prototype.terminate = function (signal) {
|
||||
this.childProcess.kill(signal);
|
||||
this.terminated = true;
|
||||
return this;
|
||||
};
|
||||
|
||||
module.exports = PythonShell;
|
Reference in New Issue
Block a user