Skip to content

Commit

Permalink
Earliest barebones dumping of the sub socket to http, will improve soon
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesgao committed Oct 21, 2010
1 parent 7fee9ed commit 7100286
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 71 deletions.
Empty file.
86 changes: 86 additions & 0 deletions IPython/frontend/html/ipythonhttp.py
@@ -0,0 +1,86 @@
""" A minimal application using the Qt console-style IPython frontend.
"""

#-----------------------------------------------------------------------------
# Imports
#-----------------------------------------------------------------------------

# Local imports
from IPython.external.argparse import ArgumentParser
from IPython.frontend.html.kernelmanager import HttpKernelManager, \
IPyHttpServer, IPyHttpHandler

#-----------------------------------------------------------------------------
# Constants
#-----------------------------------------------------------------------------

LOCALHOST = '127.0.0.1'

#-----------------------------------------------------------------------------
# Main entry point
#-----------------------------------------------------------------------------

def main():
""" Entry point for application.
"""
# 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]')
kgroup.add_argument('--xreq', type=int, metavar='PORT', default=0,
help='set the XREQ channel port [default random]')
kgroup.add_argument('--sub', type=int, metavar='PORT', default=0,
help='set the SUB channel port [default random]')
kgroup.add_argument('--rep', type=int, metavar='PORT', default=0,
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'].")

wgroup = parser.add_argument_group('widget options')
wgroup.add_argument('--paging', type=str, default='inside',
choices = ['inside', 'hsplit', 'vsplit', 'none'],
help='set the paging style [default inside]')
wgroup.add_argument('--rich', action='store_true',
help='enable rich text support')
wgroup.add_argument('--gui-completion', action='store_true',
help='use a GUI widget for tab completion')

args = parser.parse_args()

# Don't let Qt or ZMQ swallow KeyboardInterupts.
import signal
signal.signal(signal.SIGINT, signal.SIG_DFL)

# Create a KernelManager and start a kernel.
kernel_manager = HttpKernelManager(xreq_address=(args.ip, args.xreq),
sub_address=(args.ip, args.sub),
rep_address=(args.ip, args.rep),
hb_address=(args.ip, args.hb))
if args.ip == LOCALHOST and not args.existing:
if args.pure:
kernel_manager.start_kernel(ipython=False)
elif args.pylab:
kernel_manager.start_kernel(pylab=args.pylab)
else:
kernel_manager.start_kernel()
kernel_manager.start_channels()

#Start the web server
server = IPyHttpServer((args.ip, 8080), IPyHttpHandler)
server.serve_forever()


if __name__ == '__main__':
main()
132 changes: 61 additions & 71 deletions IPython/frontend/html/kernelmanager.py
Expand Up @@ -4,8 +4,62 @@
from IPython.utils.traitlets import Type
from IPython.zmq.kernelmanager import KernelManager, SubSocketChannel, \
XReqSocketChannel, RepSocketChannel, HBSocketChannel

import time
import json
import Queue
import threading
from string import Template
from SocketServer import ThreadingMixIn
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler

class CometManager(object):
def __init__(self):
self.clients = {}

def register(self, client_id):
self.clients[client_id] = Queue.Queue()

def get(self, client_id):
return self.clients[client_id].get()

def append(self, msg):
for q in self.clients.values():
q.put(msg)

def __contains__(self, item):
return item in self.clients

manager = CometManager()

class IPyHttpHandler(BaseHTTPRequestHandler):
def do_GET(self):
client_id = self.path.strip("/")
if self.path == "/notebook":
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()

client_id = str(time.time())
manager.register(client_id)
page_text = Template(open("notebook.html").read())

self.wfile.write(page_text.safe_substitute(client_id = client_id))
elif client_id in manager:
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()

#Manager.get blocks until a message is available
msg = json.dumps(manager.get(client_id))
self.wfile.write(msg)
else:
self.send_response(404)
self.end_headers()

class IPyHttpServer(ThreadingMixIn, HTTPServer):
pass

class HttpXReqSocketChannel(XReqSocketChannel):
# Used by the first_reply signal logic to determine if a reply is the
# first.
Expand All @@ -18,17 +72,7 @@ class HttpXReqSocketChannel(XReqSocketChannel):
def call_handlers(self, msg):
""" Reimplemented to emit signals instead of making callbacks.
"""
# Emit the generic signal.
self.message_received.emit(msg)

# Emit signals for specialized message types.
msg_type = msg['msg_type']
signal = getattr(self, msg_type, None)
if signal:
signal.emit(msg)

if not self._handlers_called:
self.first_reply.emit()
self._handlers_called = True

#---------------------------------------------------------------------------
Expand All @@ -41,49 +85,28 @@ def reset_first_reply(self):
self._handlers_called = False


class HttpSubSocketChannel(SocketChannelQObject, SubSocketChannel):

class HttpSubSocketChannel(SubSocketChannel):
#---------------------------------------------------------------------------
# 'SubSocketChannel' interface
#---------------------------------------------------------------------------

def call_handlers(self, msg):
""" Reimplemented to emit signals instead of making callbacks.
"""

# Emit signals for specialized message types.
msg_type = msg['msg_type']
signal = getattr(self, msg_type + '_received', None)
if signal:
signal.emit(msg)
elif msg_type in ('stdout', 'stderr'):
self.stream_received.emit(msg)

def flush(self):
""" Reimplemented to ensure that signals are dispatched immediately.
"""
super(HttpSubSocketChannel, self).flush()


class HttpRepSocketChannel(SocketChannelQObject, RepSocketChannel):
manager.append(msg)

class HttpRepSocketChannel(RepSocketChannel):
#---------------------------------------------------------------------------
# 'RepSocketChannel' interface
#---------------------------------------------------------------------------

def call_handlers(self, msg):
""" Reimplemented to emit signals instead of making callbacks.
"""
# Emit the generic signal.
self.message_received.emit(msg)

# Emit signals for specialized message types.
msg_type = msg['msg_type']
if msg_type == 'input_request':
self.input_requested.emit(msg)
pass


class HttpHBSocketChannel(SocketChannelQObject, HBSocketChannel):
class HttpHBSocketChannel(HBSocketChannel):
#---------------------------------------------------------------------------
# 'HBSocketChannel' interface
#---------------------------------------------------------------------------
Expand All @@ -92,10 +115,10 @@ def call_handlers(self, since_last_heartbeat):
""" Reimplemented to emit signals instead of making callbacks.
"""
# Emit the generic signal.
self.kernel_died.emit(since_last_heartbeat)
pass


class HttpKernelManager(KernelManager, SuperQObject):
class HttpKernelManager(KernelManager):
""" A KernelManager that provides signals and slots.
"""

Expand All @@ -105,36 +128,3 @@ class HttpKernelManager(KernelManager, SuperQObject):
rep_channel_class = Type(HttpRepSocketChannel)
hb_channel_class = Type(HttpHBSocketChannel)

#---------------------------------------------------------------------------
# 'KernelManager' interface
#---------------------------------------------------------------------------

#------ Kernel process management ------------------------------------------

def start_kernel(self, *args, **kw):
""" Reimplemented for proper heartbeat management.
"""
if self._xreq_channel is not None:
self._xreq_channel.reset_first_reply()
super(HttpKernelManager, self).start_kernel(*args, **kw)

@property
def xreq_channel(self):
""" Reimplemented for proper heartbeat management.
"""
if self._xreq_channel is None:
self._xreq_channel = super(HttpKernelManager, self).xreq_channel
self._xreq_channel.first_reply.connect(self._first_reply)
return self._xreq_channel

#---------------------------------------------------------------------------
# Protected interface
#---------------------------------------------------------------------------

def _first_reply(self):
""" Unpauses the heartbeat channel when the first reply is received on
the execute channel. Note that this will *not* start the heartbeat
channel if it is not already running!
"""
if self._hb_channel is not None:
self._hb_channel.unpause()
36 changes: 36 additions & 0 deletions IPython/frontend/html/notebook.html
@@ -0,0 +1,36 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>AJAX test</title>
<script type="text/javascript" src="http://www/~james/js/jquery.js"></script>
<script type="text/javascript">
client_id = "/$client_id";
$.ajaxSetup({
url:client_id,
dataType:"json"
})
function comet() {
$.ajax({
success: function(json, status, request) {
msg = ""
if (json.msg_type == "stream")
msg += json.content.data
else if (json.msg_type == "pyin")
msg += json.content.code

$("#messages").append("<pre>"+msg+"</pre>")
comet()
}
})
}
$(document).ready(function() {
comet()
})
</script>
</head>
<body>
<div id="messages"></div>
</body>
</html>

0 comments on commit 7100286

Please sign in to comment.