-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
151 lines (116 loc) · 3.44 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
const { spawn } = require('child_process');
const events = require('events');
const path = require('path');
class Runner extends events {
constructor({ bin, cwd, debug }) {
super();
// Which python to run
this.bin = bin || process.env.PYTHON_BIN || "python";
// Working directory for windows shell
this.cwd = cwd || process.env.PYTHON_CWD || __dirname;
// Shows a full io of shell
this.debug = debug;
this.queue = [];
// Flag of a suspence
this.freestr = '>>>';
// State params
this.started = false;
this.execution = this.connect();
if (this.execution) {
this.observe(this.execution);
this.emit('spawn');
}
}
connect() {
var execution = null;
if (process.env.NODE_ENV !== 'production') {
console.log('python-runner: PYTHON_BIN=' + this.bin)
console.log('python-runner: PYTHON_CWD=' + this.cwd);
}
try {
execution = spawn(this.bin, ['-u', '-i'], { cwd: this.cwd });
execution.on('error', (err) => {
if (process.env.NODE_ENV !== 'production') {
console.log('python-runner: start failed by reason ', err.message);
}
this.emit('fail', err);
})
} catch (err) { this.emit('fail', err); }
return execution
}
observe(execution) {
// Next queue order item execution
this.exec = () => {
// If queue is empty
if (this.queue.length < 1) {
return false
}
// Pair is combination of command and own callback
this.pair = this.queue.shift();
const cmd = this.pair.cmd + "\n";
if (this.debug) {
process.stdout.write(cmd)
}
execution.stdin.write(Buffer.from(cmd));
}
const next = (buf) => {
if (this.debug) {
process.stdout.write(buf.toString());
}
// Remove newline chars
var data = (buf ? buf.toString().replace(/(?:\\[rn]|[\r\n]+)+/g, " ") : "");
// If python waits for new command then send in
if (data.indexOf(this.freestr) === 0) {
if (this.cb) {
this.cb();
this.cb = undefined
}
this.exec()
} else {
// If hello message showed
if (data.indexOf('Python') >= 0 && this.started === false) {
this.started = true;
return this.emit('start');
}
if (this.pair && typeof this.pair.cb === 'function') {
if (/error/ig.test(data)) {
this.pair.cb(true, null)
} else {
console.log('setting cb')
// Save callback for last output
var cb = this.pair.cb
this.cb = () => cb(null, data);
}
}
this.emit('data', data);
}
};
// Listen for any stream of data
execution.stdout.on('data', next);
// Python shell uses error ouput as a default, wtf?
execution.stderr.on('data', next);
}
run(cmd, cb) {
var immediate = this.queue.length === 0
if (typeof cmd === 'string') {
this.queue.push({ cmd, cb });
} else if (Array.isArray(cmd)) {
this.__insertArray(cmd, cb)
}
// If queue was empty
if (immediate) {
this.exec()
}
}
__insertArray(array, cb) {
var lastWithCallback = array.pop();
array.map((c, i) => {
this.queue.push({ cmd: c });
})
// Append last item with callback
this.queue.push({ cmd: lastWithCallback, cb });
}
}
module.exports = (options) => {
return new Runner(options)
}