##// END OF EJS Templates
* Change input mechanism: replace raw_input instead of stdin....
epatters -
Show More
@@ -192,7 +192,7 b' class FrontendWidget(HistoryConsoleWidget):'
192 xreq.execute_reply.disconnect(self._handle_execute_reply)
192 xreq.execute_reply.disconnect(self._handle_execute_reply)
193 xreq.complete_reply.disconnect(self._handle_complete_reply)
193 xreq.complete_reply.disconnect(self._handle_complete_reply)
194 xreq.object_info_reply.disconnect(self._handle_object_info_reply)
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 # Handle the case where the old kernel manager is still listening.
197 # Handle the case where the old kernel manager is still listening.
198 if self._kernel_manager.channels_running:
198 if self._kernel_manager.channels_running:
@@ -215,7 +215,7 b' class FrontendWidget(HistoryConsoleWidget):'
215 xreq.execute_reply.connect(self._handle_execute_reply)
215 xreq.execute_reply.connect(self._handle_execute_reply)
216 xreq.complete_reply.connect(self._handle_complete_reply)
216 xreq.complete_reply.connect(self._handle_complete_reply)
217 xreq.object_info_reply.connect(self._handle_object_info_reply)
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 # Handle the case where the kernel manager started channels before
220 # Handle the case where the kernel manager started channels before
221 # we connected.
221 # we connected.
@@ -325,8 +325,8 b' class FrontendWidget(HistoryConsoleWidget):'
325 self.kernel_manager.sub_channel.flush()
325 self.kernel_manager.sub_channel.flush()
326
326
327 def callback(line):
327 def callback(line):
328 self.kernel_manager.rep_channel.readline(line)
328 self.kernel_manager.rep_channel.input(line)
329 self._readline(callback=callback)
329 self._readline(req['content']['prompt'], callback=callback)
330
330
331 def _handle_sub(self, omsg):
331 def _handle_sub(self, omsg):
332 if self._hidden:
332 if self._hidden:
@@ -98,8 +98,8 b' class QtRepSocketChannel(RepSocketChannel, QtCore.QObject):'
98 # Emitted when any message is received.
98 # Emitted when any message is received.
99 message_received = QtCore.pyqtSignal(object)
99 message_received = QtCore.pyqtSignal(object)
100
100
101 # Emitted when a readline request is received.
101 # Emitted when an input request is received.
102 readline_requested = QtCore.pyqtSignal(object)
102 input_requested = QtCore.pyqtSignal(object)
103
103
104 #---------------------------------------------------------------------------
104 #---------------------------------------------------------------------------
105 # 'object' interface
105 # 'object' interface
@@ -123,8 +123,8 b' class QtRepSocketChannel(RepSocketChannel, QtCore.QObject):'
123
123
124 # Emit signals for specialized message types.
124 # Emit signals for specialized message types.
125 msg_type = msg['msg_type']
125 msg_type = msg['msg_type']
126 if msg_type == 'readline_request':
126 if msg_type == 'input_request':
127 self.readline_requested.emit(msg)
127 self.input_requested.emit(msg)
128
128
129
129
130 class QtKernelManager(KernelManager, QtCore.QObject):
130 class QtKernelManager(KernelManager, QtCore.QObject):
@@ -37,93 +37,6 b' from completer import KernelCompleter'
37 # Kernel and stream classes
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 class OutStream(object):
40 class OutStream(object):
128 """A file like object that publishes the stream to a 0MQ PUB socket."""
41 """A file like object that publishes the stream to a 0MQ PUB socket."""
129
42
@@ -215,10 +128,11 b' class DisplayHook(object):'
215
128
216 class Kernel(object):
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 self.session = session
132 self.session = session
220 self.reply_socket = reply_socket
133 self.reply_socket = reply_socket
221 self.pub_socket = pub_socket
134 self.pub_socket = pub_socket
135 self.req_socket = req_socket
222 self.user_ns = {}
136 self.user_ns = {}
223 self.history = []
137 self.history = []
224 self.compiler = CommandCompiler()
138 self.compiler = CommandCompiler()
@@ -265,7 +179,15 b' class Kernel(object):'
265
179
266 try:
180 try:
267 comp_code = self.compiler(code, '<zmq-kernel>')
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 sys.displayhook.set_parent(parent)
189 sys.displayhook.set_parent(parent)
190
269 exec comp_code in self.user_ns, self.user_ns
191 exec comp_code in self.user_ns, self.user_ns
270 except:
192 except:
271 result = u'error'
193 result = u'error'
@@ -295,6 +217,26 b' class Kernel(object):'
295 if reply_msg['content']['status'] == u'error':
217 if reply_msg['content']['status'] == u'error':
296 self.abort_queue()
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 def complete_request(self, ident, parent):
240 def complete_request(self, ident, parent):
299 matches = {'matches' : self.complete(parent),
241 matches = {'matches' : self.complete(parent),
300 'status' : 'ok'}
242 'status' : 'ok'}
@@ -345,7 +287,7 b' class Kernel(object):'
345 def start(self):
287 def start(self):
346 while True:
288 while True:
347 ident = self.reply_socket.recv()
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 msg = self.reply_socket.recv_json()
291 msg = self.reply_socket.recv_json()
350 omsg = Message(msg)
292 omsg = Message(msg)
351 print>>sys.__stdout__
293 print>>sys.__stdout__
@@ -362,7 +304,7 b' class Kernel(object):'
362
304
363 class ExitPollerUnix(Thread):
305 class ExitPollerUnix(Thread):
364 """ A Unix-specific daemon thread that terminates the program immediately
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 def __init__(self):
310 def __init__(self):
@@ -452,13 +394,12 b' def main():'
452 print >>sys.__stdout__, "REQ Channel on port", req_port
394 print >>sys.__stdout__, "REQ Channel on port", req_port
453
395
454 # Redirect input streams and set a display hook.
396 # Redirect input streams and set a display hook.
455 sys.stdin = InStream(session, req_socket)
456 sys.stdout = OutStream(session, pub_socket, u'stdout')
397 sys.stdout = OutStream(session, pub_socket, u'stdout')
457 sys.stderr = OutStream(session, pub_socket, u'stderr')
398 sys.stderr = OutStream(session, pub_socket, u'stderr')
458 sys.displayhook = DisplayHook(session, pub_socket)
399 sys.displayhook = DisplayHook(session, pub_socket)
459
400
460 # Create the kernel.
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 # Configure this kernel/process to die on parent termination, if necessary.
404 # Configure this kernel/process to die on parent termination, if necessary.
464 if namespace.parent:
405 if namespace.parent:
@@ -368,16 +368,10 b' class RepSocketChannel(ZmqSocketChannel):'
368 """
368 """
369 raise NotImplementedError('call_handlers must be defined in a subclass.')
369 raise NotImplementedError('call_handlers must be defined in a subclass.')
370
370
371 def readline(self, line):
371 def input(self, string):
372 """A send a line of raw input to the kernel.
372 """Send a string of raw input to the kernel."""
373
373 content = dict(value=string)
374 Parameters
374 msg = self.session.msg('input_reply', content)
375 ----------
376 line : str
377 The line of the input.
378 """
379 content = dict(line=line)
380 msg = self.session.msg('readline_reply', content)
381 self._queue_reply(msg)
375 self._queue_reply(msg)
382
376
383 def _handle_events(self, socket, events):
377 def _handle_events(self, socket, events):
@@ -432,13 +426,15 b' class KernelManager(HasTraits):'
432 # The Session to use for communication with the kernel.
426 # The Session to use for communication with the kernel.
433 session = Instance(Session)
427 session = Instance(Session)
434
428
429 # The kernel process with which the KernelManager is communicating.
430 kernel = Instance(Popen)
431
435 # The classes to use for the various channels.
432 # The classes to use for the various channels.
436 xreq_channel_class = Type(XReqSocketChannel)
433 xreq_channel_class = Type(XReqSocketChannel)
437 sub_channel_class = Type(SubSocketChannel)
434 sub_channel_class = Type(SubSocketChannel)
438 rep_channel_class = Type(RepSocketChannel)
435 rep_channel_class = Type(RepSocketChannel)
439
436
440 # Protected traits.
437 # Protected traits.
441 _kernel = Instance(Popen)
442 _xreq_address = Any
438 _xreq_address = Any
443 _sub_address = Any
439 _sub_address = Any
444 _rep_address = Any
440 _rep_address = Any
@@ -504,9 +500,8 b' class KernelManager(HasTraits):'
504 "Make sure that the '*_address' attributes are "
500 "Make sure that the '*_address' attributes are "
505 "configured properly.")
501 "configured properly.")
506
502
507 kernel, xrep, pub, req = launch_kernel(
503 self.kernel, xrep, pub, req = launch_kernel(
508 xrep_port=xreq[1], pub_port=sub[1], req_port=rep[1])
504 xrep_port=xreq[1], pub_port=sub[1], req_port=rep[1])
509 self._kernel = kernel
510 self._xreq_address = (LOCALHOST, xrep)
505 self._xreq_address = (LOCALHOST, xrep)
511 self._sub_address = (LOCALHOST, pub)
506 self._sub_address = (LOCALHOST, pub)
512 self._rep_address = (LOCALHOST, req)
507 self._rep_address = (LOCALHOST, req)
@@ -515,31 +510,29 b' class KernelManager(HasTraits):'
515 def has_kernel(self):
510 def has_kernel(self):
516 """Returns whether a kernel process has been specified for the kernel
511 """Returns whether a kernel process has been specified for the kernel
517 manager.
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 def kill_kernel(self):
516 def kill_kernel(self):
524 """ Kill the running kernel. """
517 """ Kill the running kernel. """
525 if self._kernel is not None:
518 if self.kernel is not None:
526 self._kernel.kill()
519 self.kernel.kill()
527 self._kernel = None
520 self.kernel = None
528 else:
521 else:
529 raise RuntimeError("Cannot kill kernel. No kernel is running!")
522 raise RuntimeError("Cannot kill kernel. No kernel is running!")
530
523
531 def signal_kernel(self, signum):
524 def signal_kernel(self, signum):
532 """ Sends a signal to the kernel. """
525 """ Sends a signal to the kernel. """
533 if self._kernel is not None:
526 if self.kernel is not None:
534 self._kernel.send_signal(signum)
527 self.kernel.send_signal(signum)
535 else:
528 else:
536 raise RuntimeError("Cannot signal kernel. No kernel is running!")
529 raise RuntimeError("Cannot signal kernel. No kernel is running!")
537
530
538 @property
531 @property
539 def is_alive(self):
532 def is_alive(self):
540 """Is the kernel process still running?"""
533 """Is the kernel process still running?"""
541 if self._kernel is not None:
534 if self.kernel is not None:
542 if self._kernel.poll() is None:
535 if self.kernel.poll() is None:
543 return True
536 return True
544 else:
537 else:
545 return False
538 return False
General Comments 0
You need to be logged in to leave comments. Login now