frontend.py
199 lines
| 6.3 KiB
| text/x-python
|
PythonLexer
Fernando Perez
|
r2597 | #!/usr/bin/env python | ||
"""A simple interactive frontend that talks to a kernel over 0MQ. | ||||
""" | ||||
#----------------------------------------------------------------------------- | ||||
# Imports | ||||
#----------------------------------------------------------------------------- | ||||
Matthias BUSSONNIER
|
r7817 | from __future__ import print_function | ||
Fernando Perez
|
r2597 | # stdlib | ||
import cPickle as pickle | ||||
import code | ||||
import readline | ||||
import sys | ||||
import time | ||||
import uuid | ||||
# our own | ||||
import zmq | ||||
import session | ||||
import completer | ||||
MinRK
|
r3144 | from IPython.utils.localinterfaces import LOCALHOST | ||
MinRK
|
r9372 | from IPython.kernel.zmq.session import Message | ||
Fernando Perez
|
r2597 | |||
#----------------------------------------------------------------------------- | ||||
# Classes and functions | ||||
#----------------------------------------------------------------------------- | ||||
class Console(code.InteractiveConsole): | ||||
def __init__(self, locals=None, filename="<console>", | ||||
session = session, | ||||
request_socket=None, | ||||
sub_socket=None): | ||||
code.InteractiveConsole.__init__(self, locals, filename) | ||||
self.session = session | ||||
self.request_socket = request_socket | ||||
self.sub_socket = sub_socket | ||||
self.backgrounded = 0 | ||||
self.messages = {} | ||||
# Set tab completion | ||||
self.completer = completer.ClientCompleter(self, session, request_socket) | ||||
readline.parse_and_bind('tab: complete') | ||||
readline.parse_and_bind('set show-all-if-ambiguous on') | ||||
readline.set_completer(self.completer.complete) | ||||
# Set system prompts | ||||
sys.ps1 = 'Py>>> ' | ||||
sys.ps2 = ' ... ' | ||||
sys.ps3 = 'Out : ' | ||||
# Build dict of handlers for message types | ||||
self.handlers = {} | ||||
for msg_type in ['pyin', 'pyout', 'pyerr', 'stream']: | ||||
self.handlers[msg_type] = getattr(self, 'handle_%s' % msg_type) | ||||
def handle_pyin(self, omsg): | ||||
if omsg.parent_header.session == self.session.session: | ||||
return | ||||
c = omsg.content.code.rstrip() | ||||
if c: | ||||
Matthias BUSSONNIER
|
r7817 | print('[IN from %s]' % omsg.parent_header.username) | ||
print(c) | ||||
Fernando Perez
|
r2597 | |||
def handle_pyout(self, omsg): | ||||
#print omsg # dbg | ||||
if omsg.parent_header.session == self.session.session: | ||||
Matthias BUSSONNIER
|
r7817 | print("%s%s" % (sys.ps3, omsg.content.data)) | ||
Fernando Perez
|
r2597 | else: | ||
Matthias BUSSONNIER
|
r7817 | print('[Out from %s]' % omsg.parent_header.username) | ||
print(omsg.content.data) | ||||
Fernando Perez
|
r2597 | |||
def print_pyerr(self, err): | ||||
Matthias BUSSONNIER
|
r7817 | print(err.etype,':', err.evalue, file=sys.stderr) | ||
print(''.join(err.traceback), file=sys.stderr) | ||||
Fernando Perez
|
r2597 | |||
def handle_pyerr(self, omsg): | ||||
if omsg.parent_header.session == self.session.session: | ||||
return | ||||
Matthias BUSSONNIER
|
r7817 | print('[ERR from %s]' % omsg.parent_header.username, file=sys.stderr) | ||
Fernando Perez
|
r2597 | self.print_pyerr(omsg.content) | ||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2597 | def handle_stream(self, omsg): | ||
if omsg.content.name == 'stdout': | ||||
outstream = sys.stdout | ||||
else: | ||||
outstream = sys.stderr | ||||
Matthias BUSSONNIER
|
r7817 | print('*ERR*', end=' ', file=outstream) | ||
print(omsg.content.data, end=' ', file=outstream) | ||||
Fernando Perez
|
r2597 | |||
def handle_output(self, omsg): | ||||
handler = self.handlers.get(omsg.msg_type, None) | ||||
if handler is not None: | ||||
handler(omsg) | ||||
def recv_output(self): | ||||
while True: | ||||
MinRK
|
r3269 | ident,msg = self.session.recv(self.sub_socket) | ||
if msg is None: | ||||
Fernando Perez
|
r2597 | break | ||
MinRK
|
r3269 | self.handle_output(Message(msg)) | ||
Fernando Perez
|
r2597 | |||
def handle_reply(self, rep): | ||||
# Handle any side effects on output channels | ||||
self.recv_output() | ||||
# Now, dispatch on the possible reply types we must handle | ||||
if rep is None: | ||||
return | ||||
if rep.content.status == 'error': | ||||
Bernardo B. Marques
|
r4872 | self.print_pyerr(rep.content) | ||
Fernando Perez
|
r2597 | elif rep.content.status == 'aborted': | ||
Matthias BUSSONNIER
|
r7817 | print("ERROR: ABORTED", file=sys.stderr) | ||
Fernando Perez
|
r2597 | ab = self.messages[rep.parent_header.msg_id].content | ||
if 'code' in ab: | ||||
Matthias BUSSONNIER
|
r7817 | print(ab.code, file=sys.stderr) | ||
Fernando Perez
|
r2597 | else: | ||
Matthias BUSSONNIER
|
r7817 | print(ab, file=sys.stderr) | ||
Fernando Perez
|
r2597 | |||
def recv_reply(self): | ||||
MinRK
|
r3269 | ident,rep = self.session.recv(self.request_socket) | ||
mrep = Message(rep) | ||||
self.handle_reply(mrep) | ||||
return mrep | ||||
Fernando Perez
|
r2597 | |||
def runcode(self, code): | ||||
# We can't pickle code objects, so fetch the actual source | ||||
src = '\n'.join(self.buffer) | ||||
# for non-background inputs, if we do have previoiusly backgrounded | ||||
# jobs, check to see if they've produced results | ||||
if not src.endswith(';'): | ||||
while self.backgrounded > 0: | ||||
#print 'checking background' | ||||
rep = self.recv_reply() | ||||
if rep: | ||||
self.backgrounded -= 1 | ||||
time.sleep(0.05) | ||||
# Send code execution message to kernel | ||||
omsg = self.session.send(self.request_socket, | ||||
'execute_request', dict(code=src)) | ||||
self.messages[omsg.header.msg_id] = omsg | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2597 | # Fake asynchronicity by letting the user put ';' at the end of the line | ||
if src.endswith(';'): | ||||
self.backgrounded += 1 | ||||
return | ||||
# For foreground jobs, wait for reply | ||||
while True: | ||||
rep = self.recv_reply() | ||||
if rep is not None: | ||||
break | ||||
self.recv_output() | ||||
time.sleep(0.05) | ||||
else: | ||||
# We exited without hearing back from the kernel! | ||||
Matthias BUSSONNIER
|
r7817 | print('ERROR!!! kernel never got back to us!!!', file=sys.stderr) | ||
Fernando Perez
|
r2597 | |||
class InteractiveClient(object): | ||||
def __init__(self, session, request_socket, sub_socket): | ||||
self.session = session | ||||
self.request_socket = request_socket | ||||
self.sub_socket = sub_socket | ||||
self.console = Console(None, '<zmq-console>', | ||||
session, request_socket, sub_socket) | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2597 | def interact(self): | ||
self.console.interact() | ||||
def main(): | ||||
# Defaults | ||||
#ip = '192.168.2.109' | ||||
MinRK
|
r3144 | ip = LOCALHOST | ||
Fernando Perez
|
r2597 | #ip = '99.146.222.252' | ||
port_base = 5575 | ||||
connection = ('tcp://%s' % ip) + ':%i' | ||||
req_conn = connection % port_base | ||||
sub_conn = connection % (port_base+1) | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2597 | # Create initial sockets | ||
c = zmq.Context() | ||||
MinRK
|
r4725 | request_socket = c.socket(zmq.DEALER) | ||
Fernando Perez
|
r2597 | request_socket.connect(req_conn) | ||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2597 | sub_socket = c.socket(zmq.SUB) | ||
sub_socket.connect(sub_conn) | ||||
sub_socket.setsockopt(zmq.SUBSCRIBE, '') | ||||
# Make session and user-facing client | ||||
sess = session.Session() | ||||
client = InteractiveClient(sess, request_socket, sub_socket) | ||||
client.interact() | ||||
if __name__ == '__main__': | ||||
main() | ||||