@@ -30,6 +30,17 @@ function extend(obj) {
3030 * @constructor
3131 */
3232var PythonShell = function ( script , options ) {
33+
34+ function resolve ( type , val ) {
35+ if ( typeof val === 'string' ) {
36+ // use a built-in function using its name
37+ return PythonShell [ type ] [ val ] ;
38+ } else if ( typeof val === 'function' ) {
39+ // use a custom function
40+ return val ;
41+ }
42+ }
43+
3344 var self = this ;
3445 var errorData = '' ;
3546 EventEmitter . call ( this ) ;
@@ -42,19 +53,20 @@ var PythonShell = function (script, options) {
4253 this . script = path . join ( options . scriptPath || './python' , script ) ;
4354 this . command = pythonOptions . concat ( this . script , scriptArgs ) ;
4455 this . mode = options . mode || 'text' ;
45- this . parser = options . parser ;
56+ this . formatter = resolve ( 'format' , options . formatter || this . mode ) ;
57+ this . parser = resolve ( 'parse' , options . parser || this . mode ) ;
4658 this . terminated = false ;
4759 this . childProcess = spawn ( pythonPath , this . command , options ) ;
4860
4961 [ 'stdout' , 'stdin' , 'stderr' ] . forEach ( function ( name ) {
5062 self [ name ] = self . childProcess [ name ] ;
51- self . mode !== 'binary' && self [ name ] . setEncoding ( 'utf8' ) ;
63+ self . parser && self [ name ] . setEncoding ( options . encoding || 'utf8' ) ;
5264 } ) ;
5365
54- // listen for incoming data on stdout
55- this . stdout . on ( 'data' , function ( data ) {
56- self . mode !== 'binary' && self . receive ( data ) ;
57- } ) ;
66+ // parse incoming data on stdout
67+ if ( this . parser ) {
68+ this . stdout . on ( 'data' , PythonShell . prototype . receive . bind ( this ) ) ;
69+ }
5870
5971 // listen to stderr and emit errors for incoming data
6072 this . stderr . on ( 'data' , function ( data ) {
@@ -92,6 +104,27 @@ util.inherits(PythonShell, EventEmitter);
92104// allow global overrides for options
93105PythonShell . defaultOptions = { } ;
94106
107+ // built-in formatters
108+ PythonShell . format = {
109+ text : function toText ( data ) {
110+ if ( typeof data !== 'string' ) return data . toString ( ) ;
111+ return data ;
112+ } ,
113+ json : function toJson ( data ) {
114+ return JSON . stringify ( data ) ;
115+ }
116+ } ;
117+
118+ // built-in parsers
119+ PythonShell . parse = {
120+ text : function asText ( data ) {
121+ return data ;
122+ } ,
123+ json : function asJson ( data ) {
124+ return JSON . parse ( data ) ;
125+ }
126+ } ;
127+
95128/**
96129 * Runs a Python script and returns collected messages
97130 * @param {string } script The script to execute
@@ -144,22 +177,14 @@ PythonShell.prototype.parseError = function (data) {
144177
145178/**
146179 * Sends a message to the Python shell through stdin
147- * This method
148180 * Override this method to format data to be sent to the Python process
149181 * @param {string|Object } data The message to send
150182 * @returns {PythonShell } The same instance for chaining calls
151183 */
152184PythonShell . prototype . send = function ( message ) {
153- if ( this . mode === 'binary' ) {
154- throw new Error ( 'cannot send a message in binary mode, use stdin directly instead' ) ;
155- } else if ( this . mode === 'json' ) {
156- // write a JSON formatted message
157- this . stdin . write ( JSON . stringify ( message ) + '\n' ) ;
158- } else {
159- // write text-based message (default)
160- if ( typeof message !== 'string' ) message = message . toString ( ) ;
161- this . stdin . write ( message + '\n' ) ;
162- }
185+ var data = this . formatter ? this . formatter ( message ) : message ;
186+ if ( this . mode !== 'binary' ) data += '\n' ;
187+ this . stdin . write ( data ) ;
163188 return this ;
164189} ;
165190
@@ -171,41 +196,28 @@ PythonShell.prototype.send = function (message) {
171196 */
172197PythonShell . prototype . receive = function ( data ) {
173198 var self = this ;
174- var lines = ( '' + data ) . split ( / \n / g) ;
199+ var parts = ( '' + data ) . split ( / \n / g) ;
175200
176- if ( lines . length === 1 ) {
201+ if ( parts . length === 1 ) {
177202 // an incomplete record, keep buffering
178- this . _remaining = ( this . _remaining || '' ) + lines [ 0 ] ;
203+ this . _remaining = ( this . _remaining || '' ) + parts [ 0 ] ;
179204 return this ;
180205 }
181206
182- var lastLine = lines . pop ( ) ;
207+ var lastLine = parts . pop ( ) ;
183208 // fix the first line with the remaining from the previous iteration of 'receive'
184- lines [ 0 ] = ( this . _remaining || '' ) + lines [ 0 ] ;
209+ parts [ 0 ] = ( this . _remaining || '' ) + parts [ 0 ] ;
185210 // keep the remaining for the next iteration of 'receive'
186211 this . _remaining = lastLine ;
187212
188- lines . forEach ( function ( line ) {
189- if ( self . parser ) {
190- try {
191- self . emit ( 'message' , self . parser ( line ) ) ;
192- } catch ( err ) {
193- self . emit ( 'error' , extend (
194- new Error ( 'invalid message: ' + data + ' >> ' + err ) ,
195- { inner : err , data : line }
196- ) ) ;
197- }
198- } else if ( self . mode === 'json' ) {
199- try {
200- self . emit ( 'message' , JSON . parse ( line ) ) ;
201- } catch ( err ) {
202- self . emit ( 'error' , extend (
203- new Error ( 'invalid JSON message: ' + data + ' >> ' + err ) ,
204- { inner : err , data : line }
205- ) ) ;
206- }
207- } else {
208- self . emit ( 'message' , line ) ;
213+ parts . forEach ( function ( part ) {
214+ try {
215+ self . emit ( 'message' , self . parser ( part ) ) ;
216+ } catch ( err ) {
217+ self . emit ( 'error' , extend (
218+ new Error ( 'invalid message: ' + data + ' >> ' + err ) ,
219+ { inner : err , data : part }
220+ ) ) ;
209221 }
210222 } ) ;
211223
0 commit comments