Show More
@@ -6,14 +6,13 b' Useful for test suites and blocking terminal interfaces.' | |||||
6 | # Copyright (c) IPython Development Team. |
|
6 | # Copyright (c) IPython Development Team. | |
7 | # Distributed under the terms of the Modified BSD License. |
|
7 | # Distributed under the terms of the Modified BSD License. | |
8 |
|
8 | |||
9 | import atexit |
|
|||
10 | import zmq |
|
|||
11 |
|
||||
12 | try: |
|
9 | try: | |
13 | from queue import Queue, Empty # Py 3 |
|
10 | from queue import Queue, Empty # Py 3 | |
14 | except ImportError: |
|
11 | except ImportError: | |
15 | from Queue import Queue, Empty # Py 2 |
|
12 | from Queue import Queue, Empty # Py 2 | |
16 |
|
13 | |||
|
14 | from IPython.kernel.channelsabc import ShellChannelABC, IOPubChannelABC, \ | |||
|
15 | StdInChannelABC | |||
17 | from IPython.kernel.channels import HBChannel,\ |
|
16 | from IPython.kernel.channels import HBChannel,\ | |
18 | make_iopub_socket, make_shell_socket, make_stdin_socket,\ |
|
17 | make_iopub_socket, make_shell_socket, make_stdin_socket,\ | |
19 | InvalidPortNumber, major_protocol_version |
|
18 | InvalidPortNumber, major_protocol_version | |
@@ -368,6 +367,10 b' class BlockingStdInChannel(ZMQSocketChannel):' | |||||
368 | msg = self.session.msg('input_reply', content) |
|
367 | msg = self.session.msg('input_reply', content) | |
369 | self._queue_send(msg) |
|
368 | self._queue_send(msg) | |
370 |
|
369 | |||
|
370 | ShellChannelABC.register(BlockingShellChannel) | |||
|
371 | IOPubChannelABC.register(BlockingIOPubChannel) | |||
|
372 | StdInChannelABC.register(BlockingStdInChannel) | |||
|
373 | ||||
371 |
|
374 | |||
372 | class BlockingHBChannel(HBChannel): |
|
375 | class BlockingHBChannel(HBChannel): | |
373 |
|
376 |
@@ -180,326 +180,6 b' class ZMQSocketChannel(Thread):' | |||||
180 | self.call_handlers(msg) |
|
180 | self.call_handlers(msg) | |
181 |
|
181 | |||
182 |
|
182 | |||
183 |
|
||||
184 | class ShellChannel(ZMQSocketChannel): |
|
|||
185 | """The shell channel for issuing request/replies to the kernel.""" |
|
|||
186 |
|
||||
187 | command_queue = None |
|
|||
188 | # flag for whether execute requests should be allowed to call raw_input: |
|
|||
189 | allow_stdin = True |
|
|||
190 | proxy_methods = [ |
|
|||
191 | 'execute', |
|
|||
192 | 'complete', |
|
|||
193 | 'inspect', |
|
|||
194 | 'history', |
|
|||
195 | 'kernel_info', |
|
|||
196 | 'shutdown', |
|
|||
197 | 'is_complete', |
|
|||
198 | ] |
|
|||
199 |
|
||||
200 | def __init__(self, context, session, address): |
|
|||
201 | super(ShellChannel, self).__init__(context, session, address) |
|
|||
202 | self.ioloop = ioloop.IOLoop() |
|
|||
203 |
|
||||
204 | def run(self): |
|
|||
205 | """The thread's main activity. Call start() instead.""" |
|
|||
206 | self.socket = self.context.socket(zmq.DEALER) |
|
|||
207 | self.socket.linger = 1000 |
|
|||
208 | self.socket.setsockopt(zmq.IDENTITY, self.session.bsession) |
|
|||
209 | self.socket.connect(self.address) |
|
|||
210 | self.stream = zmqstream.ZMQStream(self.socket, self.ioloop) |
|
|||
211 | self.stream.on_recv(self._handle_recv) |
|
|||
212 | self._run_loop() |
|
|||
213 |
|
||||
214 | def call_handlers(self, msg): |
|
|||
215 | """This method is called in the ioloop thread when a message arrives. |
|
|||
216 |
|
||||
217 | Subclasses should override this method to handle incoming messages. |
|
|||
218 | It is important to remember that this method is called in the thread |
|
|||
219 | so that some logic must be done to ensure that the application level |
|
|||
220 | handlers are called in the application thread. |
|
|||
221 | """ |
|
|||
222 | raise NotImplementedError('call_handlers must be defined in a subclass.') |
|
|||
223 |
|
||||
224 | def execute(self, code, silent=False, store_history=True, |
|
|||
225 | user_expressions=None, allow_stdin=None): |
|
|||
226 | """Execute code in the kernel. |
|
|||
227 |
|
||||
228 | Parameters |
|
|||
229 | ---------- |
|
|||
230 | code : str |
|
|||
231 | A string of Python code. |
|
|||
232 |
|
||||
233 | silent : bool, optional (default False) |
|
|||
234 | If set, the kernel will execute the code as quietly possible, and |
|
|||
235 | will force store_history to be False. |
|
|||
236 |
|
||||
237 | store_history : bool, optional (default True) |
|
|||
238 | If set, the kernel will store command history. This is forced |
|
|||
239 | to be False if silent is True. |
|
|||
240 |
|
||||
241 | user_expressions : dict, optional |
|
|||
242 | A dict mapping names to expressions to be evaluated in the user's |
|
|||
243 | dict. The expression values are returned as strings formatted using |
|
|||
244 | :func:`repr`. |
|
|||
245 |
|
||||
246 | allow_stdin : bool, optional (default self.allow_stdin) |
|
|||
247 | Flag for whether the kernel can send stdin requests to frontends. |
|
|||
248 |
|
||||
249 | Some frontends (e.g. the Notebook) do not support stdin requests. |
|
|||
250 | If raw_input is called from code executed from such a frontend, a |
|
|||
251 | StdinNotImplementedError will be raised. |
|
|||
252 |
|
||||
253 | Returns |
|
|||
254 | ------- |
|
|||
255 | The msg_id of the message sent. |
|
|||
256 | """ |
|
|||
257 | if user_expressions is None: |
|
|||
258 | user_expressions = {} |
|
|||
259 | if allow_stdin is None: |
|
|||
260 | allow_stdin = self.allow_stdin |
|
|||
261 |
|
||||
262 |
|
||||
263 | # Don't waste network traffic if inputs are invalid |
|
|||
264 | if not isinstance(code, string_types): |
|
|||
265 | raise ValueError('code %r must be a string' % code) |
|
|||
266 | validate_string_dict(user_expressions) |
|
|||
267 |
|
||||
268 | # Create class for content/msg creation. Related to, but possibly |
|
|||
269 | # not in Session. |
|
|||
270 | content = dict(code=code, silent=silent, store_history=store_history, |
|
|||
271 | user_expressions=user_expressions, |
|
|||
272 | allow_stdin=allow_stdin, |
|
|||
273 | ) |
|
|||
274 | msg = self.session.msg('execute_request', content) |
|
|||
275 | self._queue_send(msg) |
|
|||
276 | return msg['header']['msg_id'] |
|
|||
277 |
|
||||
278 | def complete(self, code, cursor_pos=None): |
|
|||
279 | """Tab complete text in the kernel's namespace. |
|
|||
280 |
|
||||
281 | Parameters |
|
|||
282 | ---------- |
|
|||
283 | code : str |
|
|||
284 | The context in which completion is requested. |
|
|||
285 | Can be anything between a variable name and an entire cell. |
|
|||
286 | cursor_pos : int, optional |
|
|||
287 | The position of the cursor in the block of code where the completion was requested. |
|
|||
288 | Default: ``len(code)`` |
|
|||
289 |
|
||||
290 | Returns |
|
|||
291 | ------- |
|
|||
292 | The msg_id of the message sent. |
|
|||
293 | """ |
|
|||
294 | if cursor_pos is None: |
|
|||
295 | cursor_pos = len(code) |
|
|||
296 | content = dict(code=code, cursor_pos=cursor_pos) |
|
|||
297 | msg = self.session.msg('complete_request', content) |
|
|||
298 | self._queue_send(msg) |
|
|||
299 | return msg['header']['msg_id'] |
|
|||
300 |
|
||||
301 | def inspect(self, code, cursor_pos=None, detail_level=0): |
|
|||
302 | """Get metadata information about an object in the kernel's namespace. |
|
|||
303 |
|
||||
304 | It is up to the kernel to determine the appropriate object to inspect. |
|
|||
305 |
|
||||
306 | Parameters |
|
|||
307 | ---------- |
|
|||
308 | code : str |
|
|||
309 | The context in which info is requested. |
|
|||
310 | Can be anything between a variable name and an entire cell. |
|
|||
311 | cursor_pos : int, optional |
|
|||
312 | The position of the cursor in the block of code where the info was requested. |
|
|||
313 | Default: ``len(code)`` |
|
|||
314 | detail_level : int, optional |
|
|||
315 | The level of detail for the introspection (0-2) |
|
|||
316 |
|
||||
317 | Returns |
|
|||
318 | ------- |
|
|||
319 | The msg_id of the message sent. |
|
|||
320 | """ |
|
|||
321 | if cursor_pos is None: |
|
|||
322 | cursor_pos = len(code) |
|
|||
323 | content = dict(code=code, cursor_pos=cursor_pos, |
|
|||
324 | detail_level=detail_level, |
|
|||
325 | ) |
|
|||
326 | msg = self.session.msg('inspect_request', content) |
|
|||
327 | self._queue_send(msg) |
|
|||
328 | return msg['header']['msg_id'] |
|
|||
329 |
|
||||
330 | def history(self, raw=True, output=False, hist_access_type='range', **kwargs): |
|
|||
331 | """Get entries from the kernel's history list. |
|
|||
332 |
|
||||
333 | Parameters |
|
|||
334 | ---------- |
|
|||
335 | raw : bool |
|
|||
336 | If True, return the raw input. |
|
|||
337 | output : bool |
|
|||
338 | If True, then return the output as well. |
|
|||
339 | hist_access_type : str |
|
|||
340 | 'range' (fill in session, start and stop params), 'tail' (fill in n) |
|
|||
341 | or 'search' (fill in pattern param). |
|
|||
342 |
|
||||
343 | session : int |
|
|||
344 | For a range request, the session from which to get lines. Session |
|
|||
345 | numbers are positive integers; negative ones count back from the |
|
|||
346 | current session. |
|
|||
347 | start : int |
|
|||
348 | The first line number of a history range. |
|
|||
349 | stop : int |
|
|||
350 | The final (excluded) line number of a history range. |
|
|||
351 |
|
||||
352 | n : int |
|
|||
353 | The number of lines of history to get for a tail request. |
|
|||
354 |
|
||||
355 | pattern : str |
|
|||
356 | The glob-syntax pattern for a search request. |
|
|||
357 |
|
||||
358 | Returns |
|
|||
359 | ------- |
|
|||
360 | The msg_id of the message sent. |
|
|||
361 | """ |
|
|||
362 | content = dict(raw=raw, output=output, hist_access_type=hist_access_type, |
|
|||
363 | **kwargs) |
|
|||
364 | msg = self.session.msg('history_request', content) |
|
|||
365 | self._queue_send(msg) |
|
|||
366 | return msg['header']['msg_id'] |
|
|||
367 |
|
||||
368 | def kernel_info(self): |
|
|||
369 | """Request kernel info.""" |
|
|||
370 | msg = self.session.msg('kernel_info_request') |
|
|||
371 | self._queue_send(msg) |
|
|||
372 | return msg['header']['msg_id'] |
|
|||
373 |
|
||||
374 | def _handle_kernel_info_reply(self, msg): |
|
|||
375 | """handle kernel info reply |
|
|||
376 |
|
||||
377 | sets protocol adaptation version |
|
|||
378 | """ |
|
|||
379 | adapt_version = int(msg['content']['protocol_version'].split('.')[0]) |
|
|||
380 | if adapt_version != major_protocol_version: |
|
|||
381 | self.session.adapt_version = adapt_version |
|
|||
382 |
|
||||
383 | def shutdown(self, restart=False): |
|
|||
384 | """Request an immediate kernel shutdown. |
|
|||
385 |
|
||||
386 | Upon receipt of the (empty) reply, client code can safely assume that |
|
|||
387 | the kernel has shut down and it's safe to forcefully terminate it if |
|
|||
388 | it's still alive. |
|
|||
389 |
|
||||
390 | The kernel will send the reply via a function registered with Python's |
|
|||
391 | atexit module, ensuring it's truly done as the kernel is done with all |
|
|||
392 | normal operation. |
|
|||
393 | """ |
|
|||
394 | # Send quit message to kernel. Once we implement kernel-side setattr, |
|
|||
395 | # this should probably be done that way, but for now this will do. |
|
|||
396 | msg = self.session.msg('shutdown_request', {'restart':restart}) |
|
|||
397 | self._queue_send(msg) |
|
|||
398 | return msg['header']['msg_id'] |
|
|||
399 |
|
||||
400 | def is_complete(self, code): |
|
|||
401 | msg = self.session.msg('is_complete_request', {'code': code}) |
|
|||
402 | self._queue_send(msg) |
|
|||
403 | return msg['header']['msg_id'] |
|
|||
404 |
|
||||
405 |
|
||||
406 | class IOPubChannel(ZMQSocketChannel): |
|
|||
407 | """The iopub channel which listens for messages that the kernel publishes. |
|
|||
408 |
|
||||
409 | This channel is where all output is published to frontends. |
|
|||
410 | """ |
|
|||
411 |
|
||||
412 | def __init__(self, context, session, address): |
|
|||
413 | super(IOPubChannel, self).__init__(context, session, address) |
|
|||
414 | self.ioloop = ioloop.IOLoop() |
|
|||
415 |
|
||||
416 | def run(self): |
|
|||
417 | """The thread's main activity. Call start() instead.""" |
|
|||
418 | self.socket = self.context.socket(zmq.SUB) |
|
|||
419 | self.socket.linger = 1000 |
|
|||
420 | self.socket.setsockopt(zmq.SUBSCRIBE,b'') |
|
|||
421 | self.socket.setsockopt(zmq.IDENTITY, self.session.bsession) |
|
|||
422 | self.socket.connect(self.address) |
|
|||
423 | self.stream = zmqstream.ZMQStream(self.socket, self.ioloop) |
|
|||
424 | self.stream.on_recv(self._handle_recv) |
|
|||
425 | self._run_loop() |
|
|||
426 |
|
||||
427 | def call_handlers(self, msg): |
|
|||
428 | """This method is called in the ioloop thread when a message arrives. |
|
|||
429 |
|
||||
430 | Subclasses should override this method to handle incoming messages. |
|
|||
431 | It is important to remember that this method is called in the thread |
|
|||
432 | so that some logic must be done to ensure that the application leve |
|
|||
433 | handlers are called in the application thread. |
|
|||
434 | """ |
|
|||
435 | raise NotImplementedError('call_handlers must be defined in a subclass.') |
|
|||
436 |
|
||||
437 | def flush(self, timeout=1.0): |
|
|||
438 | """Immediately processes all pending messages on the iopub channel. |
|
|||
439 |
|
||||
440 | Callers should use this method to ensure that :meth:`call_handlers` |
|
|||
441 | has been called for all messages that have been received on the |
|
|||
442 | 0MQ SUB socket of this channel. |
|
|||
443 |
|
||||
444 | This method is thread safe. |
|
|||
445 |
|
||||
446 | Parameters |
|
|||
447 | ---------- |
|
|||
448 | timeout : float, optional |
|
|||
449 | The maximum amount of time to spend flushing, in seconds. The |
|
|||
450 | default is one second. |
|
|||
451 | """ |
|
|||
452 | # We do the IOLoop callback process twice to ensure that the IOLoop |
|
|||
453 | # gets to perform at least one full poll. |
|
|||
454 | stop_time = time.time() + timeout |
|
|||
455 | for i in range(2): |
|
|||
456 | self._flushed = False |
|
|||
457 | self.ioloop.add_callback(self._flush) |
|
|||
458 | while not self._flushed and time.time() < stop_time: |
|
|||
459 | time.sleep(0.01) |
|
|||
460 |
|
||||
461 | def _flush(self): |
|
|||
462 | """Callback for :method:`self.flush`.""" |
|
|||
463 | self.stream.flush() |
|
|||
464 | self._flushed = True |
|
|||
465 |
|
||||
466 |
|
||||
467 | class StdInChannel(ZMQSocketChannel): |
|
|||
468 | """The stdin channel to handle raw_input requests that the kernel makes.""" |
|
|||
469 |
|
||||
470 | msg_queue = None |
|
|||
471 | proxy_methods = ['input'] |
|
|||
472 |
|
||||
473 | def __init__(self, context, session, address): |
|
|||
474 | super(StdInChannel, self).__init__(context, session, address) |
|
|||
475 | self.ioloop = ioloop.IOLoop() |
|
|||
476 |
|
||||
477 | def run(self): |
|
|||
478 | """The thread's main activity. Call start() instead.""" |
|
|||
479 | self.socket = self.context.socket(zmq.DEALER) |
|
|||
480 | self.socket.linger = 1000 |
|
|||
481 | self.socket.setsockopt(zmq.IDENTITY, self.session.bsession) |
|
|||
482 | self.socket.connect(self.address) |
|
|||
483 | self.stream = zmqstream.ZMQStream(self.socket, self.ioloop) |
|
|||
484 | self.stream.on_recv(self._handle_recv) |
|
|||
485 | self._run_loop() |
|
|||
486 |
|
||||
487 | def call_handlers(self, msg): |
|
|||
488 | """This method is called in the ioloop thread when a message arrives. |
|
|||
489 |
|
||||
490 | Subclasses should override this method to handle incoming messages. |
|
|||
491 | It is important to remember that this method is called in the thread |
|
|||
492 | so that some logic must be done to ensure that the application leve |
|
|||
493 | handlers are called in the application thread. |
|
|||
494 | """ |
|
|||
495 | raise NotImplementedError('call_handlers must be defined in a subclass.') |
|
|||
496 |
|
||||
497 | def input(self, string): |
|
|||
498 | """Send a string of raw input to the kernel.""" |
|
|||
499 | content = dict(value=string) |
|
|||
500 | msg = self.session.msg('input_reply', content) |
|
|||
501 | self._queue_send(msg) |
|
|||
502 |
|
||||
503 | def make_shell_socket(context, identity, address): |
|
183 | def make_shell_socket(context, identity, address): | |
504 | socket = context.socket(zmq.DEALER) |
|
184 | socket = context.socket(zmq.DEALER) | |
505 | socket.linger = 1000 |
|
185 | socket.linger = 1000 | |
@@ -655,11 +335,4 b' class HBChannel(ZMQSocketChannel):' | |||||
655 | raise NotImplementedError('call_handlers must be defined in a subclass.') |
|
335 | raise NotImplementedError('call_handlers must be defined in a subclass.') | |
656 |
|
336 | |||
657 |
|
337 | |||
658 | #---------------------------------------------------------------------#----------------------------------------------------------------------------- |
|
|||
659 | # ABC Registration |
|
|||
660 | #----------------------------------------------------------------------------- |
|
|||
661 |
|
||||
662 | ShellChannelABC.register(ShellChannel) |
|
|||
663 | IOPubChannelABC.register(IOPubChannel) |
|
|||
664 | HBChannelABC.register(HBChannel) |
|
338 | HBChannelABC.register(HBChannel) | |
665 | StdInChannelABC.register(StdInChannel) |
|
@@ -11,10 +11,8 b' from IPython.utils.traitlets import (' | |||||
11 | Any, Instance, Type, |
|
11 | Any, Instance, Type, | |
12 | ) |
|
12 | ) | |
13 |
|
13 | |||
14 | from .zmq.session import Session |
|
14 | from .channelsabc import ( | |
15 | from .channels import ( |
|
15 | ShellChannelABC, IOPubChannelABC, HBChannelABC, StdInChannelABC | |
16 | ShellChannel, IOPubChannel, |
|
|||
17 | HBChannel, StdInChannel, |
|
|||
18 | ) |
|
16 | ) | |
19 | from .clientabc import KernelClientABC |
|
17 | from .clientabc import KernelClientABC | |
20 | from .connect import ConnectionFileMixin |
|
18 | from .connect import ConnectionFileMixin | |
@@ -42,10 +40,10 b' class KernelClient(ConnectionFileMixin):' | |||||
42 | return zmq.Context.instance() |
|
40 | return zmq.Context.instance() | |
43 |
|
41 | |||
44 | # The classes to use for the various channels |
|
42 | # The classes to use for the various channels | |
45 | shell_channel_class = Type(ShellChannel) |
|
43 | shell_channel_class = Type(ShellChannelABC) | |
46 | iopub_channel_class = Type(IOPubChannel) |
|
44 | iopub_channel_class = Type(IOPubChannelABC) | |
47 | stdin_channel_class = Type(StdInChannel) |
|
45 | stdin_channel_class = Type(StdInChannelABC) | |
48 | hb_channel_class = Type(HBChannel) |
|
46 | hb_channel_class = Type(HBChannelABC) | |
49 |
|
47 | |||
50 | # Protected traits |
|
48 | # Protected traits | |
51 | _shell_channel = Any |
|
49 | _shell_channel = Any |
General Comments 0
You need to be logged in to leave comments.
Login now