##// END OF EJS Templates
* Change input mechanism: replace raw_input instead of stdin....
epatters -
Show More
@@ -192,7 +192,7 class FrontendWidget(HistoryConsoleWidget):
192 192 xreq.execute_reply.disconnect(self._handle_execute_reply)
193 193 xreq.complete_reply.disconnect(self._handle_complete_reply)
194 194 xreq.object_info_reply.disconnect(self._handle_object_info_reply)
195 rep.readline_requested.disconnect(self._handle_req)
195 rep.input_requested.disconnect(self._handle_req)
196 196
197 197 # Handle the case where the old kernel manager is still listening.
198 198 if self._kernel_manager.channels_running:
@@ -215,7 +215,7 class FrontendWidget(HistoryConsoleWidget):
215 215 xreq.execute_reply.connect(self._handle_execute_reply)
216 216 xreq.complete_reply.connect(self._handle_complete_reply)
217 217 xreq.object_info_reply.connect(self._handle_object_info_reply)
218 rep.readline_requested.connect(self._handle_req)
218 rep.input_requested.connect(self._handle_req)
219 219
220 220 # Handle the case where the kernel manager started channels before
221 221 # we connected.
@@ -325,8 +325,8 class FrontendWidget(HistoryConsoleWidget):
325 325 self.kernel_manager.sub_channel.flush()
326 326
327 327 def callback(line):
328 self.kernel_manager.rep_channel.readline(line)
329 self._readline(callback=callback)
328 self.kernel_manager.rep_channel.input(line)
329 self._readline(req['content']['prompt'], callback=callback)
330 330
331 331 def _handle_sub(self, omsg):
332 332 if self._hidden:
@@ -98,8 +98,8 class QtRepSocketChannel(RepSocketChannel, QtCore.QObject):
98 98 # Emitted when any message is received.
99 99 message_received = QtCore.pyqtSignal(object)
100 100
101 # Emitted when a readline request is received.
102 readline_requested = QtCore.pyqtSignal(object)
101 # Emitted when an input request is received.
102 input_requested = QtCore.pyqtSignal(object)
103 103
104 104 #---------------------------------------------------------------------------
105 105 # 'object' interface
@@ -123,8 +123,8 class QtRepSocketChannel(RepSocketChannel, QtCore.QObject):
123 123
124 124 # Emit signals for specialized message types.
125 125 msg_type = msg['msg_type']
126 if msg_type == 'readline_request':
127 self.readline_requested.emit(msg)
126 if msg_type == 'input_request':
127 self.input_requested.emit(msg)
128 128
129 129
130 130 class QtKernelManager(KernelManager, QtCore.QObject):
@@ -37,93 +37,6 from completer import KernelCompleter
37 37 # Kernel and stream classes
38 38 #-----------------------------------------------------------------------------
39 39
40 class InStream(object):
41 """ A file like object that reads from a 0MQ XREQ socket."""
42
43 def __init__(self, session, socket):
44 self.session = session
45 self.socket = socket
46
47 def close(self):
48 self.socket = None
49
50 def flush(self):
51 if self.socket is None:
52 raise ValueError('I/O operation on closed file')
53
54 def isatty(self):
55 return False
56
57 def next(self):
58 raise IOError('Seek not supported.')
59
60 def read(self, size=-1):
61 # FIXME: Do we want another request for this?
62 string = '\n'.join(self.readlines())
63 return self._truncate(string, size)
64
65 def readline(self, size=-1):
66 if self.socket is None:
67 raise ValueError('I/O operation on closed file')
68 else:
69 content = dict(size=size)
70 msg = self.session.msg('readline_request', content=content)
71 reply = self._request(msg)
72 line = reply['content']['line']
73 return self._truncate(line, size)
74
75 def readlines(self, sizehint=-1):
76 # Sizehint is ignored, as is permitted.
77 if self.socket is None:
78 raise ValueError('I/O operation on closed file')
79 else:
80 lines = []
81 while True:
82 line = self.readline()
83 if line:
84 lines.append(line)
85 else:
86 break
87 return lines
88
89 def seek(self, offset, whence=None):
90 raise IOError('Seek not supported.')
91
92 def write(self, string):
93 raise IOError('Write not supported on a read only stream.')
94
95 def writelines(self, sequence):
96 raise IOError('Write not supported on a read only stream.')
97
98 def _request(self, msg):
99 # Flush output before making the request. This ensures, for example,
100 # that raw_input(prompt) actually gets a prompt written.
101 sys.stderr.flush()
102 sys.stdout.flush()
103
104 self.socket.send_json(msg)
105 while True:
106 try:
107 reply = self.socket.recv_json(zmq.NOBLOCK)
108 except zmq.ZMQError, e:
109 if e.errno == zmq.EAGAIN:
110 pass
111 else:
112 raise
113 else:
114 break
115 return reply
116
117 def _truncate(self, string, size):
118 if size >= 0:
119 if isinstance(string, str):
120 return string[:size]
121 elif isinstance(string, unicode):
122 encoded = string.encode('utf-8')[:size]
123 return encoded.decode('utf-8', 'ignore')
124 return string
125
126
127 40 class OutStream(object):
128 41 """A file like object that publishes the stream to a 0MQ PUB socket."""
129 42
@@ -215,10 +128,11 class DisplayHook(object):
215 128
216 129 class Kernel(object):
217 130
218 def __init__(self, session, reply_socket, pub_socket):
131 def __init__(self, session, reply_socket, pub_socket, req_socket):
219 132 self.session = session
220 133 self.reply_socket = reply_socket
221 134 self.pub_socket = pub_socket
135 self.req_socket = req_socket
222 136 self.user_ns = {}
223 137 self.history = []
224 138 self.compiler = CommandCompiler()
@@ -265,7 +179,15 class Kernel(object):
265 179
266 180 try:
267 181 comp_code = self.compiler(code, '<zmq-kernel>')
182
183 # Replace raw_input. Note that is not sufficient to replace
184 # raw_input in the user namespace.
185 raw_input = lambda prompt='': self.raw_input(prompt, ident, parent)
186 __builtin__.raw_input = raw_input
187
188 # Configure the display hook.
268 189 sys.displayhook.set_parent(parent)
190
269 191 exec comp_code in self.user_ns, self.user_ns
270 192 except:
271 193 result = u'error'
@@ -295,6 +217,26 class Kernel(object):
295 217 if reply_msg['content']['status'] == u'error':
296 218 self.abort_queue()
297 219
220 def raw_input(self, prompt, ident, parent):
221 # Flush output before making the request.
222 sys.stderr.flush()
223 sys.stdout.flush()
224
225 # Send the input request.
226 content = dict(prompt=prompt)
227 msg = self.session.msg(u'input_request', content, parent)
228 self.req_socket.send_json(msg)
229
230 # Await a response.
231 reply = self.req_socket.recv_json()
232 try:
233 value = reply['content']['value']
234 except:
235 print>>sys.__stderr__, "Got bad raw_input reply: "
236 print>>sys.__stderr__, Message(parent)
237 value = ''
238 return value
239
298 240 def complete_request(self, ident, parent):
299 241 matches = {'matches' : self.complete(parent),
300 242 'status' : 'ok'}
@@ -345,7 +287,7 class Kernel(object):
345 287 def start(self):
346 288 while True:
347 289 ident = self.reply_socket.recv()
348 assert self.reply_socket.rcvmore(), "Unexpected missing message part."
290 assert self.reply_socket.rcvmore(), "Missing message part."
349 291 msg = self.reply_socket.recv_json()
350 292 omsg = Message(msg)
351 293 print>>sys.__stdout__
@@ -362,7 +304,7 class Kernel(object):
362 304
363 305 class ExitPollerUnix(Thread):
364 306 """ A Unix-specific daemon thread that terminates the program immediately
365 when this process' parent process no longer exists.
307 when the parent process no longer exists.
366 308 """
367 309
368 310 def __init__(self):
@@ -452,13 +394,12 def main():
452 394 print >>sys.__stdout__, "REQ Channel on port", req_port
453 395
454 396 # Redirect input streams and set a display hook.
455 sys.stdin = InStream(session, req_socket)
456 397 sys.stdout = OutStream(session, pub_socket, u'stdout')
457 398 sys.stderr = OutStream(session, pub_socket, u'stderr')
458 399 sys.displayhook = DisplayHook(session, pub_socket)
459 400
460 401 # Create the kernel.
461 kernel = Kernel(session, reply_socket, pub_socket)
402 kernel = Kernel(session, reply_socket, pub_socket, req_socket)
462 403
463 404 # Configure this kernel/process to die on parent termination, if necessary.
464 405 if namespace.parent:
@@ -368,16 +368,10 class RepSocketChannel(ZmqSocketChannel):
368 368 """
369 369 raise NotImplementedError('call_handlers must be defined in a subclass.')
370 370
371 def readline(self, line):
372 """A send a line of raw input to the kernel.
373
374 Parameters
375 ----------
376 line : str
377 The line of the input.
378 """
379 content = dict(line=line)
380 msg = self.session.msg('readline_reply', content)
371 def input(self, string):
372 """Send a string of raw input to the kernel."""
373 content = dict(value=string)
374 msg = self.session.msg('input_reply', content)
381 375 self._queue_reply(msg)
382 376
383 377 def _handle_events(self, socket, events):
@@ -432,13 +426,15 class KernelManager(HasTraits):
432 426 # The Session to use for communication with the kernel.
433 427 session = Instance(Session)
434 428
429 # The kernel process with which the KernelManager is communicating.
430 kernel = Instance(Popen)
431
435 432 # The classes to use for the various channels.
436 433 xreq_channel_class = Type(XReqSocketChannel)
437 434 sub_channel_class = Type(SubSocketChannel)
438 435 rep_channel_class = Type(RepSocketChannel)
439 436
440 437 # Protected traits.
441 _kernel = Instance(Popen)
442 438 _xreq_address = Any
443 439 _sub_address = Any
444 440 _rep_address = Any
@@ -504,9 +500,8 class KernelManager(HasTraits):
504 500 "Make sure that the '*_address' attributes are "
505 501 "configured properly.")
506 502
507 kernel, xrep, pub, req = launch_kernel(
503 self.kernel, xrep, pub, req = launch_kernel(
508 504 xrep_port=xreq[1], pub_port=sub[1], req_port=rep[1])
509 self._kernel = kernel
510 505 self._xreq_address = (LOCALHOST, xrep)
511 506 self._sub_address = (LOCALHOST, pub)
512 507 self._rep_address = (LOCALHOST, req)
@@ -515,31 +510,29 class KernelManager(HasTraits):
515 510 def has_kernel(self):
516 511 """Returns whether a kernel process has been specified for the kernel
517 512 manager.
518
519 A kernel process can be set via 'start_kernel' or 'set_kernel'.
520 513 """
521 return self._kernel is not None
514 return self.kernel is not None
522 515
523 516 def kill_kernel(self):
524 517 """ Kill the running kernel. """
525 if self._kernel is not None:
526 self._kernel.kill()
527 self._kernel = None
518 if self.kernel is not None:
519 self.kernel.kill()
520 self.kernel = None
528 521 else:
529 522 raise RuntimeError("Cannot kill kernel. No kernel is running!")
530 523
531 524 def signal_kernel(self, signum):
532 525 """ Sends a signal to the kernel. """
533 if self._kernel is not None:
534 self._kernel.send_signal(signum)
526 if self.kernel is not None:
527 self.kernel.send_signal(signum)
535 528 else:
536 529 raise RuntimeError("Cannot signal kernel. No kernel is running!")
537 530
538 531 @property
539 532 def is_alive(self):
540 533 """Is the kernel process still running?"""
541 if self._kernel is not None:
542 if self._kernel.poll() is None:
534 if self.kernel is not None:
535 if self.kernel.poll() is None:
543 536 return True
544 537 else:
545 538 return False
General Comments 0
You need to be logged in to leave comments. Login now