Show More
@@ -6,14 +6,13 b' Useful for test suites and blocking terminal interfaces.' | |||
|
6 | 6 | # Copyright (c) IPython Development Team. |
|
7 | 7 | # Distributed under the terms of the Modified BSD License. |
|
8 | 8 | |
|
9 | import atexit | |
|
10 | import zmq | |
|
11 | ||
|
12 | 9 | try: |
|
13 | 10 | from queue import Queue, Empty # Py 3 |
|
14 | 11 | except ImportError: |
|
15 | 12 | from Queue import Queue, Empty # Py 2 |
|
16 | 13 | |
|
14 | from IPython.kernel.channelsabc import ShellChannelABC, IOPubChannelABC, \ | |
|
15 | StdInChannelABC | |
|
17 | 16 | from IPython.kernel.channels import HBChannel,\ |
|
18 | 17 | make_iopub_socket, make_shell_socket, make_stdin_socket,\ |
|
19 | 18 | InvalidPortNumber, major_protocol_version |
@@ -368,6 +367,10 b' class BlockingStdInChannel(ZMQSocketChannel):' | |||
|
368 | 367 | msg = self.session.msg('input_reply', content) |
|
369 | 368 | self._queue_send(msg) |
|
370 | 369 | |
|
370 | ShellChannelABC.register(BlockingShellChannel) | |
|
371 | IOPubChannelABC.register(BlockingIOPubChannel) | |
|
372 | StdInChannelABC.register(BlockingStdInChannel) | |
|
373 | ||
|
371 | 374 | |
|
372 | 375 | class BlockingHBChannel(HBChannel): |
|
373 | 376 |
@@ -180,326 +180,6 b' class ZMQSocketChannel(Thread):' | |||
|
180 | 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 | 183 | def make_shell_socket(context, identity, address): |
|
504 | 184 | socket = context.socket(zmq.DEALER) |
|
505 | 185 | socket.linger = 1000 |
@@ -655,11 +335,4 b' class HBChannel(ZMQSocketChannel):' | |||
|
655 | 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 | 338 | HBChannelABC.register(HBChannel) |
|
665 | StdInChannelABC.register(StdInChannel) |
@@ -11,10 +11,8 b' from IPython.utils.traitlets import (' | |||
|
11 | 11 | Any, Instance, Type, |
|
12 | 12 | ) |
|
13 | 13 | |
|
14 | from .zmq.session import Session | |
|
15 | from .channels import ( | |
|
16 | ShellChannel, IOPubChannel, | |
|
17 | HBChannel, StdInChannel, | |
|
14 | from .channelsabc import ( | |
|
15 | ShellChannelABC, IOPubChannelABC, HBChannelABC, StdInChannelABC | |
|
18 | 16 | ) |
|
19 | 17 | from .clientabc import KernelClientABC |
|
20 | 18 | from .connect import ConnectionFileMixin |
@@ -42,10 +40,10 b' class KernelClient(ConnectionFileMixin):' | |||
|
42 | 40 | return zmq.Context.instance() |
|
43 | 41 | |
|
44 | 42 | # The classes to use for the various channels |
|
45 | shell_channel_class = Type(ShellChannel) | |
|
46 | iopub_channel_class = Type(IOPubChannel) | |
|
47 | stdin_channel_class = Type(StdInChannel) | |
|
48 | hb_channel_class = Type(HBChannel) | |
|
43 | shell_channel_class = Type(ShellChannelABC) | |
|
44 | iopub_channel_class = Type(IOPubChannelABC) | |
|
45 | stdin_channel_class = Type(StdInChannelABC) | |
|
46 | hb_channel_class = Type(HBChannelABC) | |
|
49 | 47 | |
|
50 | 48 | # Protected traits |
|
51 | 49 | _shell_channel = Any |
General Comments 0
You need to be logged in to leave comments.
Login now