UNPKG

13.6 kBMarkdownView Raw
1# [python-shell](https://www.npmjs.com/package/python-shell) [![Build status](https://ci.appveyor.com/api/projects/status/m8e3h53vvxg5wb2q/branch/master?svg=true)](https://ci.appveyor.com/project/Almenon/python-shell/branch/master) [![codecov](https://codecov.io/gh/extrabacon/python-shell/branch/master/graph/badge.svg)](https://codecov.io/gh/extrabacon/python-shell)
2
3<!-- change above url according to repo -->
4A simple way to run Python scripts from Node.js with basic but efficient inter-process communication and better error handling.
5
6## Features
7
8+ Reliably spawn Python scripts in a child process
9+ Built-in text, JSON and binary modes
10+ Custom parsers and formatters
11+ Simple and efficient data transfers through stdin and stdout streams
12+ Extended stack traces when an error is thrown
13
14## Requirements
15First make sure you are able to run `python3` (Mac/Linux) or `python` (Windows) from the terminal. If you are not then you might need to add it to the PATH. If you want to use a version of python not in the PATH you should specify `options.pythonPath`.
16
17## Installation
18
19```bash
20npm install python-shell
21```
22
23## Documentation
24
25### Running python code:
26
27```typescript
28import {PythonShell} from 'python-shell';
29
30PythonShell.runString('x=1+1;print(x)', null, function (err) {
31 if (err) throw err;
32 console.log('finished');
33});
34```
35
36If the script exits with a non-zero code, an error will be thrown.
37
38Note the use of imports! If you're not using typescript ಠ_ಠ you can [still get imports to work with this guide](https://github.com/extrabacon/python-shell/issues/148#issuecomment-419120209).
39
40Or you can use require like so:
41```javascript
42let {PythonShell} = require('python-shell')
43```
44
45### Running a Python script:
46
47```typescript
48import {PythonShell} from 'python-shell';
49
50PythonShell.run('my_script.py', null, function (err) {
51 if (err) throw err;
52 console.log('finished');
53});
54```
55
56If the script exits with a non-zero code, an error will be thrown.
57
58### Running a Python script with arguments and options:
59
60```typescript
61import {PythonShell} from 'python-shell';
62
63let options = {
64 mode: 'text',
65 pythonPath: 'path/to/python',
66 pythonOptions: ['-u'], // get print results in real-time
67 scriptPath: 'path/to/my/scripts',
68 args: ['value1', 'value2', 'value3']
69};
70
71PythonShell.run('my_script.py', options, function (err, results) {
72 if (err) throw err;
73 // results is an array consisting of messages collected during execution
74 console.log('results: %j', results);
75});
76```
77
78### Exchanging data between Node and Python:
79
80```typescript
81import {PythonShell} from 'python-shell';
82let pyshell = new PythonShell('my_script.py');
83
84// sends a message to the Python script via stdin
85pyshell.send('hello');
86
87pyshell.on('message', function (message) {
88 // received a message sent from the Python script (a simple "print" statement)
89 console.log(message);
90});
91
92// end the input stream and allow the process to exit
93pyshell.end(function (err,code,signal) {
94 if (err) throw err;
95 console.log('The exit code was: ' + code);
96 console.log('The exit signal was: ' + signal);
97 console.log('finished');
98});
99```
100
101Use `.send(message)` to send a message to the Python script. Attach the `message` event to listen to messages emitted from the Python script.
102
103Use `options.mode` to quickly setup how data is sent and received between your Node and Python applications.
104
105 * use `text` mode for exchanging lines of text ending with a [newline character](http://hayne.net/MacDev/Notes/unixFAQ.html#endOfLine).
106 * use `json` mode for exchanging JSON fragments
107 * use `binary` mode for anything else (data is sent and received as-is)
108
109Stderr always uses text mode.
110
111For more details and examples including Python source code, take a look at the tests.
112
113### Error Handling and extended stack traces
114
115An error will be thrown if the process exits with a non-zero exit code. Additionally, if "stderr" contains a formatted Python traceback, the error is augmented with Python exception details including a concatenated stack trace.
116
117Sample error with traceback (from test/python/error.py):
118
119```
120Traceback (most recent call last):
121 File "test/python/error.py", line 6, in <module>
122 divide_by_zero()
123 File "test/python/error.py", line 4, in divide_by_zero
124 print 1/0
125ZeroDivisionError: integer division or modulo by zero
126```
127
128would result into the following error:
129
130```typescript
131{ [Error: ZeroDivisionError: integer division or modulo by zero]
132 traceback: 'Traceback (most recent call last):\n File "test/python/error.py", line 6, in <module>\n divide_by_zero()\n File "test/python/error.py", line 4, in divide_by_zero\n print 1/0\nZeroDivisionError: integer division or modulo by zero\n',
133 executable: 'python',
134 options: null,
135 script: 'test/python/error.py',
136 args: null,
137 exitCode: 1 }
138```
139
140and `err.stack` would look like this:
141
142```
143Error: ZeroDivisionError: integer division or modulo by zero
144 at PythonShell.parseError (python-shell/index.js:131:17)
145 at ChildProcess.<anonymous> (python-shell/index.js:67:28)
146 at ChildProcess.EventEmitter.emit (events.js:98:17)
147 at Process.ChildProcess._handle.onexit (child_process.js:797:12)
148 ----- Python Traceback -----
149 File "test/python/error.py", line 6, in <module>
150 divide_by_zero()
151 File "test/python/error.py", line 4, in divide_by_zero
152 print 1/0
153```
154
155## API Reference
156
157#### `PythonShell(script, options)` constructor
158
159Creates an instance of `PythonShell` and starts the Python process
160
161* `script`: the path of the script to execute
162* `options`: the execution options, consisting of:
163 * `mode`: Configures how data is exchanged when data flows through stdin and stdout. The possible values are:
164 * `text`: each line of data is emitted as a message (default)
165 * `json`: each line of data is parsed as JSON and emitted as a message
166 * `binary`: data is streamed as-is through `stdout` and `stdin`
167 * `formatter`: each message to send is transformed using this method, then appended with a newline
168 * `parser`: each line of data is parsed with this function and its result is emitted as a message
169 * `stderrParser`: each line of logs is parsed with this function and its result is emitted as a message
170 * `encoding`: the text encoding to apply on the child process streams (default: "utf8")
171 * `pythonPath`: The path where to locate the "python" executable. Default: "python3" ("python" for Windows)
172 * `pythonOptions`: Array of option switches to pass to "python"
173 * `scriptPath`: The default path where to look for scripts. Default is the current working directory.
174 * `args`: Array of arguments to pass to the script
175* `stdoutSplitter`: splits stdout into chunks, defaulting to splitting into newline-seperated lines
176* `stderrSplitter`: splits stderr into chunks, defaulting to splitting into newline-seperated lines
177
178Other options are forwarded to `child_process.spawn`.
179
180PythonShell instances have the following properties:
181* `script`: the path of the script to execute
182* `command`: the full command arguments passed to the Python executable
183* `stdin`: the Python stdin stream, used to send data to the child process
184* `stdout`: the Python stdout stream, used for receiving data from the child process
185* `stderr`: the Python stderr stream, used for communicating logs & errors
186* `childProcess`: the process instance created via `child_process.spawn`
187* `terminated`: boolean indicating whether the process has exited
188* `exitCode`: the process exit code, available after the process has ended
189
190Example:
191
192```typescript
193// create a new instance
194let shell = new PythonShell('script.py', options);
195```
196
197#### `#defaultOptions`
198
199Configures default options for all new instances of PythonShell.
200
201Example:
202
203```typescript
204// setup a default "scriptPath"
205PythonShell.defaultOptions = { scriptPath: '../scripts' };
206```
207
208#### `#run(script, options, callback)`
209
210Runs the Python script and invokes `callback` with the results. The callback contains the execution error (if any) as well as an array of messages emitted from the Python script.
211
212This method is also returning the `PythonShell` instance.
213
214Example:
215
216```typescript
217// run a simple script
218PythonShell.run('script.py', null, function (err, results) {
219 // script finished
220});
221```
222
223#### `#runString(code, options, callback)`
224
225Runs the Python code and invokes `callback` with the results. The callback contains the execution error (if any) as well as an array of messages emitted from the Python script.
226
227This method is also returning the `PythonShell` instance.
228
229Example:
230
231```typescript
232// run a simple script
233PythonShell.runString('x=1;print(x)', null, function (err, results) {
234 // script finished
235});
236```
237
238#### `#checkSyntax(code:string)`
239
240Checks the syntax of the code and returns a promise.
241Promise is rejected if there is a syntax error.
242
243#### `#checkSyntaxFile(filePath:string)`
244
245Checks the syntax of the file and returns a promise.
246Promise is rejected if there is a syntax error.
247
248#### `#getVersion(pythonPath?:string)`
249
250Returns the python version. Optional pythonPath param to get the version
251of a specific python interpreter.
252
253#### `#getVersionSync(pythonPath?:string)`
254
255Returns the python version. Optional pythonPath param to get the version
256of a specific python interpreter.
257
258#### `.send(message)`
259
260Sends a message to the Python script via stdin. The data is formatted according to the selected mode (text or JSON), or through a custom function when `formatter` is specified.
261
262Example:
263
264```typescript
265// send a message in text mode
266let shell = new PythonShell('script.py', { mode: 'text'});
267shell.send('hello world!');
268
269// send a message in JSON mode
270let shell = new PythonShell('script.py', { mode: 'json'});
271shell.send({ command: "do_stuff", args: [1, 2, 3] });
272```
273
274#### `.end(callback)`
275
276Closes the stdin stream, allowing the Python script to finish and exit. The optional callback is invoked when the process is terminated.
277
278#### `.kill(signal)`
279
280Terminates the python script. A kill signal may be provided by `signal`, if `signal` is not specified SIGTERM is sent.
281
282#### event: `message`
283
284After the stdout stream is split into chunks by stdoutSplitter the chunks are parsed by the parser and a message event is emitted for each parsed chunk. This event is not emitted in binary mode.
285
286Example:
287
288```typescript
289// receive a message in text mode
290let shell = new PythonShell('script.py', { mode: 'text'});
291shell.on('message', function (message) {
292 // handle message (a line of text from stdout)
293});
294
295// receive a message in JSON mode
296let shell = new PythonShell('script.py', { mode: 'json'});
297shell.on('message', function (message) {
298 // handle message (a line of text from stdout, parsed as JSON)
299});
300```
301
302#### event: `stderr`
303
304After the stderr stream is split into chunks by stderrSplitter the chunks are parsed by the parser and a message event is emitted for each parsed chunk. This event is not emitted in binary mode.
305
306Example:
307
308```typescript
309// receive a message in text mode
310let shell = new PythonShell('script.py', { mode: 'text'});
311shell.on('stderr', function (stderr) {
312 // handle stderr (a line of text from stderr)
313});
314```
315
316#### event: `close`
317
318Fires when the process has been terminated, with an error or not.
319
320#### event: `pythonError`
321
322Fires when the process terminates with a non-zero exit code.
323
324#### event: `error`
325
326Fires when:
327* The process could not be spawned, or
328* The process could not be killed, or
329* Sending a message to the child process failed.
330
331If the process could not be spawned please double-check that python can be launched from the terminal.
332
333### NewlineTransformer
334
335A utility class for splitting stream data into newlines. Used as the default for stdoutSplitter and stderrSplitter if they are unspecified. You can use this class for any extra python streams if you'd like.
336
337## Used By:
338
339Python-Shell is used by [arepl-vscode](https://github.com/almenon/arepl-vscode), [gitinspector](https://github.com/ejwa/gitinspector), [pyspreadsheet](https://github.com/extrabacon/pyspreadsheet), [AtlantOS Ocean Data QC](https://github.com/ocean-data-qc/ocean-data-qc) and more!
340
341## License
342
343The MIT License (MIT)
344
345Copyright (c) 2014 Nicolas Mercier
346
347Permission is hereby granted, free of charge, to any person obtaining a copy
348of this software and associated documentation files (the "Software"), to deal
349in the Software without restriction, including without limitation the rights
350to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
351copies of the Software, and to permit persons to whom the Software is
352furnished to do so, subject to the following conditions:
353
354The above copyright notice and this permission notice shall be included in
355all copies or substantial portions of the Software.
356
357THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
358IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
359FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
360AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
361LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
362OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
363THE SOFTWARE.