@@ -29,9 +29,12 @@ import json
29
29
import sys
30
30
import inspect
31
31
import os
32
+ import random
33
+ import gevent
32
34
from pprint import pprint
33
35
34
36
import zerorpc
37
+ from zerorpc import zmq
35
38
36
39
37
40
parser = argparse .ArgumentParser (
@@ -161,6 +164,111 @@ def zerorpc_inspect(client, method=None, long_doc=True, include_argspec=True):
161
164
longest_name_len = max (len (name ) for name , doc in r )
162
165
return (longest_name_len , r )
163
166
167
+ def context_shell (client ):
168
+ try :
169
+ # We want to avoid making zerorpc dependent upon gevent_subprocess until
170
+ # the context feature conquer the world and become indispensable.
171
+ import gevent_subprocess
172
+ except ImportError as e :
173
+ print e
174
+ print 'You might want to install gevent_subprocess to' ,
175
+ print 'use the sub shell feature of the zerorpc' 's cli.'
176
+ print 'ex: pip install gevent_subprocess'
177
+ return - 1
178
+
179
+ # This ugly broken proxy assume that you will never try to run more than one
180
+ # zerorpc client at a time... No one should take this code as a good
181
+ # practice! Since the whole thing is just some convenience for zerorpc's cli
182
+ # it shouldn't be so much a big deal anyway.
183
+
184
+ context_name = client ._zerorpc_name ()
185
+ class Proxy (zerorpc .SocketBase ):
186
+ def __init__ (self , channel ):
187
+ self ._channel = channel
188
+ self ._zmqid = None
189
+ self ._msgid = None
190
+ self ._sender_task = None
191
+ self ._recver_task = None
192
+ super (Proxy , self ).__init__ (zmq .XREP )
193
+
194
+ def close (self ):
195
+ if self ._sender_task :
196
+ self ._sender_task .kill ()
197
+ if self ._recver_task :
198
+ self ._recver_task .kill ()
199
+ super (Proxy , self ).close ()
200
+
201
+ def _sender (self ):
202
+ while True :
203
+ event = self ._events .recv ()
204
+ self ._zmqid = event .header .get ('zmqid' , None )
205
+ self ._msgid = event .header .get ('message_id' , None )
206
+ self ._channel .emit_event (event )
207
+
208
+ def _recver (self ):
209
+ while True :
210
+ event = self ._channel .recv ()
211
+ self ._events .emit_event (event , self ._zmqid )
212
+
213
+ def run (self ):
214
+ self ._sender_task = gevent .spawn (self ._sender )
215
+ self ._recver_task = gevent .spawn (self ._recver )
216
+
217
+ def _sender_error (self ):
218
+ # reply in loop that the context is closed...
219
+ while True :
220
+ event = self ._events .recv ()
221
+ self ._zmqid = event .header .get ('zmqid' , None )
222
+ self ._msgid = event .header .get ('message_id' , None )
223
+ self ._events .emit ('ERR' , ('ContextClosed' ,
224
+ 'Context closed! You might want to exit your sub-shell and open'
225
+ ' another one.' , None ),
226
+ dict (zmqid = self ._zmqid , response_to = self ._msgid ))
227
+
228
+ def error_mode (self , e ):
229
+ if self ._sender_task :
230
+ self ._sender_task .kill ()
231
+ self ._sender_task = None
232
+ if self ._recver_task :
233
+ self ._recver_task .kill ()
234
+ self ._recver_task = None
235
+ # send an error over the wire to stop any pending zerorpc client on
236
+ # the other side of the IPC.
237
+ self ._events .emit ('ERR' , (type (e ).__name__ , str (e ), None ),
238
+ dict (zmqid = self ._zmqid , response_to = self ._msgid ))
239
+ self ._sender_task = gevent .spawn (self ._sender_error )
240
+
241
+ # So here, we roughly inter-connect an IPC to the remote Context.
242
+ ipc_endpoint = '/tmp/zerorpc_context_proxy_{0}' .format (random .random ())
243
+ proxy_endpoint = 'ipc://{0}' .format (ipc_endpoint )
244
+ client_events = client ._multiplexer ._events
245
+ client ._multiplexer .close ()
246
+ proxy = Proxy (client_events )
247
+ proxy .bind (proxy_endpoint )
248
+ proxy .run ()
249
+
250
+ print 'You are in a subshell, zerorpc\' s cli will now connect to this sub rpc' ,
251
+ print 'context by default. To close the context, simply exit the sub-shell.'
252
+ print 'ZERORPC_CONNECT_TO="{0}"' .format (proxy_endpoint )
253
+ print '-- context "{0}" open' .format (context_name )
254
+ print
255
+ env = os .environ .copy ()
256
+ env ['ZERORPC_CONNECT_TO' ] = proxy_endpoint
257
+ subshell = gevent_subprocess .Popen (os .environ .get ('SHELL' , 'sh' ), env = env )
258
+ try :
259
+ subshell .wait ()
260
+ except Exception as e :
261
+ print '-- '
262
+ print '-- context "{0}" closed on error: {1}' .format (context_name , e )
263
+ proxy .error_mode (e )
264
+ subshell .wait ()
265
+ else :
266
+ print '-- context "{0}" closed' .format (context_name )
267
+ finally :
268
+ try :
269
+ os .unlink (ipc_endpoint )
270
+ except OSError :
271
+ pass
164
272
165
273
def run_client (args ):
166
274
client = zerorpc .Client (timeout = args .timeout , heartbeat = args .heartbeat ,
@@ -187,12 +295,10 @@ def run_client(args):
187
295
else :
188
296
call_args = args .params
189
297
results = client (args .command , * call_args )
190
- if getattr (results , 'next' , None ) is None :
191
- if args .print_json :
192
- json .dump (results , sys .stdout )
193
- else :
194
- pprint (results )
195
- else :
298
+ if isinstance (results , zerorpc .ClientBase ):
299
+ # context
300
+ return context_shell (results )
301
+ elif getattr (results , 'next' , None ) is not None :
196
302
# streaming responses
197
303
if args .print_json :
198
304
first = True
@@ -207,20 +313,29 @@ def run_client(args):
207
313
else :
208
314
for result in results :
209
315
pprint (result )
316
+ else :
317
+ # req/rep
318
+ if args .print_json :
319
+ json .dump (results , sys .stdout )
320
+ else :
321
+ pprint (results )
210
322
211
323
212
324
def main ():
213
325
args = parser .parse_args ()
214
326
215
- if args .bind or args .connect :
327
+ proxy_endpoint = os .environ .get ('ZERORPC_CONNECT_TO' , None )
328
+ if args .bind or args .connect or proxy_endpoint :
216
329
if args .command :
217
330
args .params .insert (0 , args .command )
218
331
args .command = args .address
219
332
args .address = None
220
333
221
334
if not (args .bind or args .connect or args .address ):
222
- parser .print_help ()
223
- return - 1
335
+ if not proxy_endpoint :
336
+ parser .print_help ()
337
+ return - 1
338
+ args .address = proxy_endpoint
224
339
225
340
if args .client :
226
341
return run_client (args )
0 commit comments