frontend.py
253 lines
| 9.7 KiB
| text/x-python
|
PythonLexer
Omar Andres Zapata Mesa
|
r5578 | # -*- coding: utf-8 -*- | ||
"""Frontend of ipython working with python-zmq | ||||
Ipython's frontend, is a ipython interface that send request to kernel and proccess the kernel's outputs. | ||||
For more details, see the ipython-zmq design | ||||
""" | ||||
#----------------------------------------------------------------------------- | ||||
# Copyright (C) 2010 The IPython Development Team | ||||
# | ||||
# Distributed under the terms of the BSD License. The full license is in | ||||
# the file COPYING, distributed as part of this software. | ||||
#----------------------------------------------------------------------------- | ||||
#----------------------------------------------------------------------------- | ||||
# Imports | ||||
#----------------------------------------------------------------------------- | ||||
Thomas Kluyver
|
r5598 | from __future__ import print_function | ||
Omar Andres Zapata Mesa
|
r5578 | |||
import __builtin__ | ||||
import sys | ||||
import os | ||||
Thomas Kluyver
|
r5596 | from Queue import Empty | ||
Omar Andres Zapata Mesa
|
r5583 | import readline | ||
Omar Andres Zapata Mesa
|
r5585 | import rlcompleter | ||
Omar Andres Zapata Mesa
|
r5578 | |||
#----------------------------------------------------------------------------- | ||||
# Imports from ipython | ||||
#----------------------------------------------------------------------------- | ||||
from IPython.external.argparse import ArgumentParser | ||||
Thomas Kluyver
|
r5593 | from IPython.core.inputsplitter import IPythonInputSplitter | ||
Thomas Kluyver
|
r5596 | from IPython.zmq.blockingkernelmanager import BlockingKernelManager as KernelManager | ||
MinRK
|
r5611 | from IPython.frontend.terminal.console.completer import ClientCompleter2p | ||
Omar Andres Zapata Mesa
|
r5585 | |||
Omar Andres Zapata Mesa
|
r5578 | #----------------------------------------------------------------------------- | ||
# Network Constants | ||||
#----------------------------------------------------------------------------- | ||||
from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS | ||||
class Frontend(object): | ||||
Thomas Kluyver
|
r5596 | """This class is a simple frontend to ipython-zmq | ||
Omar Andres Zapata Mesa
|
r5578 | |||
Thomas Kluyver
|
r5594 | NOTE: this class uses kernelmanager to manipulate sockets | ||
Omar Andres Zapata Mesa
|
r5578 | |||
Parameters: | ||||
----------- | ||||
kernelmanager : object | ||||
instantiated object from class KernelManager in module kernelmanager | ||||
Thomas Kluyver
|
r5596 | """ | ||
Omar Andres Zapata Mesa
|
r5578 | |||
Thomas Kluyver
|
r5596 | def __init__(self, kernelmanager): | ||
Omar Andres Zapata Mesa
|
r5578 | self.km = kernelmanager | ||
Thomas Kluyver
|
r5597 | self.session_id = self.km.session.session | ||
Thomas Kluyver
|
r5596 | self.completer = ClientCompleter2p(self, self.km) | ||
Omar Andres Zapata Mesa
|
r5583 | readline.parse_and_bind("tab: complete") | ||
readline.parse_and_bind('set show-all-if-ambiguous on') | ||||
Omar Andres Zapata Mesa
|
r5585 | readline.set_completer(self.completer.complete) | ||
Omar Andres Zapata Mesa
|
r5578 | history_path = os.path.expanduser('~/.ipython/history') | ||
if os.path.isfile(history_path): | ||||
rlcompleter.readline.read_history_file(history_path) | ||||
else: | ||||
Thomas Kluyver
|
r5594 | print("history file cannot be read.") | ||
Omar Andres Zapata Mesa
|
r5578 | |||
self.messages = {} | ||||
Omar Andres Zapata Mesa
|
r5579 | |||
Thomas Kluyver
|
r5593 | self._splitter = IPythonInputSplitter() | ||
Omar Andres Zapata Mesa
|
r5580 | self.code = "" | ||
Omar Andres Zapata Mesa
|
r5588 | |||
Thomas Kluyver
|
r5594 | self.prompt_count = 0 | ||
self._get_initial_prompt() | ||||
Omar Andres Zapata Mesa
|
r5588 | |||
Thomas Kluyver
|
r5596 | def _get_initial_prompt(self): | ||
Omar Andres Zapata Mesa
|
r5588 | self._execute('', hidden=True) | ||
Thomas Kluyver
|
r5596 | def interact(self): | ||
Thomas Kluyver
|
r5594 | """Gets input from console using inputsplitter, then | ||
Omar Andres Zapata Mesa
|
r5578 | while you enter code it can indent and set index id to any input | ||
""" | ||||
Omar Andres Zapata Mesa
|
r5580 | |||
Omar Andres Zapata Mesa
|
r5578 | try: | ||
Thomas Kluyver
|
r5598 | print() | ||
MinRK
|
r5599 | self._splitter.push(raw_input('In [%i]: '%self.prompt_count+self.code)) | ||
Omar Andres Zapata Mesa
|
r5578 | while self._splitter.push_accepts_more(): | ||
MinRK
|
r5599 | self.code = raw_input('.....: '+' '*self._splitter.indent_spaces) | ||
Omar Andres Zapata Mesa
|
r5580 | self._splitter.push(' '*self._splitter.indent_spaces+self.code) | ||
self._execute(self._splitter.source,False) | ||||
self._splitter.reset() | ||||
Thomas Kluyver
|
r5594 | except KeyboardInterrupt: | ||
Omar Andres Zapata Mesa
|
r5579 | print('\nKeyboardInterrupt\n') | ||
pass | ||||
Omar Andres Zapata Mesa
|
r5580 | |||
Omar Andres Zapata Mesa
|
r5578 | |||
Thomas Kluyver
|
r5596 | def start(self): | ||
Thomas Kluyver
|
r5594 | """Start the interaction loop, calling the .interact() method for each | ||
input cell. | ||||
Omar Andres Zapata Mesa
|
r5578 | """ | ||
while True: | ||||
try: | ||||
Omar Andres Zapata Mesa
|
r5579 | self.interact() | ||
Thomas Kluyver
|
r5594 | except KeyboardInterrupt: | ||
Omar Andres Zapata Mesa
|
r5579 | print('\nKeyboardInterrupt\n') | ||
pass | ||||
Omar Andres Zapata Mesa
|
r5578 | except EOFError: | ||
answer = '' | ||||
while True: | ||||
answer = raw_input('\nDo you really want to exit ([y]/n)?') | ||||
if answer == 'y' or answer == '' : | ||||
Thomas Kluyver
|
r5591 | self.km.shutdown_kernel() | ||
Omar Andres Zapata Mesa
|
r5578 | sys.exit() | ||
elif answer == 'n': | ||||
break | ||||
Omar Andres Zapata Mesa
|
r5585 | |||
Thomas Kluyver
|
r5596 | def _execute(self, source, hidden = True): | ||
""" Execute 'source'. If 'hidden', do not show any output. | ||||
Omar Andres Zapata Mesa
|
r5578 | |||
See parent class :meth:`execute` docstring for full details. | ||||
Thomas Kluyver
|
r5596 | """ | ||
MinRK
|
r5599 | msg_id = self.km.shell_channel.execute(source, hidden) | ||
while not self.km.shell_channel.msg_ready(): | ||||
Thomas Kluyver
|
r5596 | try: | ||
MinRK
|
r5599 | self.handle_stdin_channel(timeout=0.1) | ||
Thomas Kluyver
|
r5596 | except Empty: | ||
pass | ||||
Thomas Kluyver
|
r5597 | self.handle_execute_reply(msg_id) | ||
def handle_execute_reply(self, msg_id): | ||||
MinRK
|
r5599 | msg = self.km.shell_channel.get_msg() | ||
if msg["parent_header"]["msg_id"] == msg_id: | ||||
if msg["content"]["status"] == 'ok' : | ||||
Thomas Kluyver
|
r5597 | self.handle_sub_channel() | ||
Thomas Kluyver
|
r5596 | |||
MinRK
|
r5599 | elif msg["content"]["status"] == 'error': | ||
for frame in msg["content"]["traceback"]: | ||||
Thomas Kluyver
|
r5598 | print(frame, file=sys.stderr) | ||
MinRK
|
r5599 | self.prompt_count = msg["content"]["execution_count"] + 1 | ||
Omar Andres Zapata Mesa
|
r5579 | |||
Thomas Kluyver
|
r5596 | def handle_sub_channel(self): | ||
Omar Andres Zapata Mesa
|
r5579 | """ Method to procces subscribe channel's messages | ||
Thomas Kluyver
|
r5594 | This method reads a message and processes the content in different | ||
outputs like stdout, stderr, pyout and status | ||||
Omar Andres Zapata Mesa
|
r5579 | |||
Arguments: | ||||
sub_msg: message receive from kernel in the sub socket channel | ||||
capture by kernel manager. | ||||
""" | ||||
Thomas Kluyver
|
r5596 | while self.km.sub_channel.msg_ready(): | ||
Omar Andres Zapata Mesa
|
r5579 | sub_msg = self.km.sub_channel.get_msg() | ||
Thomas Kluyver
|
r5597 | if self.session_id == sub_msg['parent_header']['session']: | ||
Omar Andres Zapata Mesa
|
r5579 | if sub_msg['msg_type'] == 'status' : | ||
if sub_msg["content"]["execution_state"] == "busy" : | ||||
pass | ||||
Thomas Kluyver
|
r5597 | elif sub_msg['msg_type'] == 'stream' : | ||
Omar Andres Zapata Mesa
|
r5579 | if sub_msg["content"]["name"] == "stdout": | ||
Thomas Kluyver
|
r5598 | print(sub_msg["content"]["data"], file=sys.stdout, end="") | ||
Omar Andres Zapata Mesa
|
r5579 | sys.stdout.flush() | ||
Thomas Kluyver
|
r5597 | elif sub_msg["content"]["name"] == "stderr" : | ||
Thomas Kluyver
|
r5598 | print(sub_msg["content"]["data"], file=sys.stderr, end="") | ||
Omar Andres Zapata Mesa
|
r5579 | sys.stderr.flush() | ||
Thomas Kluyver
|
r5597 | elif sub_msg['msg_type'] == 'pyout' : | ||
Thomas Kluyver
|
r5598 | print("Out[%i]:"%sub_msg["content"]["execution_count"], | ||
sub_msg["content"]["data"]["text/plain"], | ||||
file=sys.stdout) | ||||
Omar Andres Zapata Mesa
|
r5579 | sys.stdout.flush() | ||
MinRK
|
r5599 | def handle_stdin_channel(self, timeout=0.1): | ||
Thomas Kluyver
|
r5596 | """ Method to capture raw_input | ||
""" | ||||
MinRK
|
r5599 | msg_rep = self.km.stdin_channel.get_msg(timeout=timeout) | ||
Thomas Kluyver
|
r5597 | if self.session_id == msg_rep["parent_header"]["session"] : | ||
raw_data = raw_input(msg_rep["content"]["prompt"]) | ||||
MinRK
|
r5599 | self.km.stdin_channel.input(raw_data) | ||
Thomas Kluyver
|
r5591 | |||
Omar Andres Zapata Mesa
|
r5583 | |||
Omar Andres Zapata Mesa
|
r5578 | |||
def start_frontend(): | ||||
""" Entry point for application. | ||||
Omar Andres Zapata Mesa
|
r5579 | |||
Omar Andres Zapata Mesa
|
r5578 | """ | ||
# Parse command line arguments. | ||||
parser = ArgumentParser() | ||||
kgroup = parser.add_argument_group('kernel options') | ||||
kgroup.add_argument('-e', '--existing', action='store_true', | ||||
help='connect to an existing kernel') | ||||
kgroup.add_argument('--ip', type=str, default=LOCALHOST, | ||||
help=\ | ||||
"set the kernel\'s IP address [default localhost].\ | ||||
If the IP address is something other than localhost, then \ | ||||
Consoles on other machines will be able to connect\ | ||||
to the Kernel, so be careful!") | ||||
MinRK
|
r5599 | kgroup.add_argument('--shell', type=int, metavar='PORT', default=0, | ||
Omar Andres Zapata Mesa
|
r5578 | help='set the XREQ channel port [default random]') | ||
MinRK
|
r5599 | kgroup.add_argument('--iopub', type=int, metavar='PORT', default=0, | ||
Omar Andres Zapata Mesa
|
r5578 | help='set the SUB channel port [default random]') | ||
MinRK
|
r5599 | kgroup.add_argument('--stdin', type=int, metavar='PORT', default=0, | ||
Omar Andres Zapata Mesa
|
r5578 | help='set the REP channel port [default random]') | ||
kgroup.add_argument('--hb', type=int, metavar='PORT', default=0, | ||||
help='set the heartbeat port [default random]') | ||||
egroup = kgroup.add_mutually_exclusive_group() | ||||
egroup.add_argument('--pure', action='store_true', help = \ | ||||
'use a pure Python kernel instead of an IPython kernel') | ||||
egroup.add_argument('--pylab', type=str, metavar='GUI', nargs='?', | ||||
const='auto', help = \ | ||||
"Pre-load matplotlib and numpy for interactive use. If GUI is not \ | ||||
given, the GUI backend is matplotlib's, otherwise use one of: \ | ||||
['tk', 'gtk', 'qt', 'wx', 'inline'].") | ||||
egroup.add_argument('--colors', type=str, | ||||
help="Set the color scheme (LightBG,Linux,NoColor). This is guessed\ | ||||
based on the pygments style if not set.") | ||||
args = parser.parse_args() | ||||
# parse the colors arg down to current known labels | ||||
if args.colors: | ||||
colors=args.colors.lower() | ||||
if colors in ('lightbg', 'light'): | ||||
colors='lightbg' | ||||
elif colors in ('dark', 'linux'): | ||||
colors='linux' | ||||
else: | ||||
colors='nocolor' | ||||
else: | ||||
colors=None | ||||
# Create a KernelManager and start a kernel. | ||||
MinRK
|
r5599 | kernel_manager = KernelManager(shell_address=(args.ip, args.shell), | ||
sub_address=(args.ip, args.iopub), | ||||
stdin_address=(args.ip, args.stdin), | ||||
Omar Andres Zapata Mesa
|
r5578 | hb_address=(args.ip, args.hb)) | ||
if not args.existing: | ||||
# if not args.ip in LOCAL_IPS+ALL_ALIAS: | ||||
# raise ValueError("Must bind a local ip, such as: %s"%LOCAL_IPS) | ||||
MinRK
|
r5599 | kwargs = dict(ip=args.ip, ipython=True) | ||
Omar Andres Zapata Mesa
|
r5578 | kernel_manager.start_kernel(**kwargs) | ||
kernel_manager.start_channels() | ||||
frontend=Frontend(kernel_manager) | ||||
return frontend | ||||
if __name__ == "__main__" : | ||||
frontend=start_frontend() | ||||
Thomas Kluyver
|
r5591 | frontend.start() | ||