Html notebook #179

Closed
wants to merge 36 commits into
from
Commits
Jump to file or symbol
Failed to load files and symbols.
+1,397 −1
Split
@@ -0,0 +1,80 @@
+"""Manages messages for Comet design pattern
+
+COMET design pattern:
+http://en.wikipedia.org/wiki/Comet_(programming)
+
+Basic idea -- webpage asks server via a GET request. Server blocks until
+something is available, then sends it along to waiting client.
+"""
+#-----------------------------------------------------------------------------
+# Imports
+#-----------------------------------------------------------------------------
+import time
+import Queue
+
+#-----------------------------------------------------------------------------
+# Constants
+#-----------------------------------------------------------------------------
+# seconds without heartbeat that client considered dead
+client_death = 300
+
+class Manager(object):
+ """Tracks msg_id, client get requests for the Comet design pattern"""
+ def __init__(self):
+ self.clients = {}
+ self.req_queue = Queue.Queue()
+
+ def register(self, client_id):
+ self.clients[client_id] = [time.time(), Queue.Queue()]
+
+ def __getitem__(self, client_id):
+ if client_id in self.clients:
+ return self.clients[client_id][1]
+ else:
+ return None
+
+ def append(self, msg):
+ """Add a message to the queues across all tracked clients"""
+ for i in self.clients.keys():
+ dead_for = time.time() - self.clients[i][0]
+ #Remove client if no heartbeat, otherwise add to its queue
+ if dead_for > client_death:
+ del self.clients[i]
+ else:
+ self.clients[i][1].put(msg)
+
+ def __contains__(self, client_id):
+ return client_id in self.clients
+
+ def heartbeat(self, client_id):
+ """Updates the client heartbeat"""
+ if client_id in self.clients:
+ self.clients[client_id][0] = time.time()
+
+ def connect(self):
+ #FIXME: better connect method to return execution_count without COMET
+ return self.kernel_manager.xreq_channel.execute("")
+
+ def execute(self, *args):
+ return self.kernel_manager.xreq_channel.execute(*args)
+
+ def complete(self, code, pos):
+ chunk = re.split('\s|\(|=|;', code[:int(pos)])[-1]
+ self.kernel_manager.xreq_channel.complete(chunk, code, pos)
+ return self.req_queue.get()
+
+ def inspect(self, oname):
+ self.kernel_manager.xreq_channel.object_info(oname)
+ return self.req_queue.get()
+
+ def history(self, index = None, raw = False, output=False):
+ if index == -1:
+ index = None
+
+ self.kernel_manager.xreq_channel.history(index, raw, output)
+ return self.req_queue.get()
+
+ def addreq(self, msg):
+ """Adds a message to the immediate-return queue
+ """
+ self.req_queue.put(msg)
No changes.
@@ -0,0 +1,18 @@
+body {
+ font-family:sans-serif;
+ font-size:10pt;
+ margin:5px;
+}
+pre, p { margin:0px; }
+.cblue { color: blue }
+.ccyan { color: teal }
+.cgreen { color: green }
+.cyellow { color: #990 }
+.cwhite { color: #777 }
+.cred { color: red }
+.cbold { font-weight:bold }
+.hidden { display:none; }
+.clear { clear:both; }
+
+.headers { font-size:60%;}
+.xreq { font-style:italic }
@@ -0,0 +1,143 @@
+#messages *,#sidebar * {
+ border-radius:3px;
+ -moz-border-radius:3px;
+}
+#sidebar {
+ width:20%;
+ height:100%;
+ position:fixed;
+ right:5px;
+ background:white;
+}
+
+.sidebox {
+ margin:6px;
+ margin-bottom:36px;
+}
+.sidebox .inside {
+ padding:8px;
+ border:1px solid #CCC;
+ min-height:20px;
+ overflow-x:hidden;
+ overflow-y:auto;
+}
+.sidebox .title {
+ margin:-15px 0px -20px 20px;
+ padding:5px;
+ float:left;
+ background:white;
+}
+
+/*
+ * History box
+ */
+#history { height:30% }
+#history .inside {
+ height:100%;
+}
+#history .item { width:1024px; }
+#history .item > p {
+ float:left;
+ margin-right:3px;
+}
+#history a {
+ color:#888;
+ text-decoration:none;
+ display:block;
+ float:left;
+}
+/*
+ * Status bar
+ */
+#statusbar {
+ position:absolute;
+ bottom:6px;
+ right:0px;
+ margin:6px;
+ padding:8px;
+ border:1px solid #CCC;
+}
+#statusbar span { vertical-align:middle; }
+#statusbar .bullet {
+ font-size:150%;
+ margin-left:10px;
+ float:right;
+}
+
+/*
+ * Messages
+ */
+#messages {
+ width:80%;
+ float:left;
+}
+#messages .selected {
+ background:#CCC;
+}
+.message {
+ border:1px solid #CCC;
+ border-radius:3px;
+ padding:8px;
+ margin:6px;
+ overflow:auto;
+}
+.message:hover {
+ border:1px solid red;
+ cursor:pointer;
+}
+.active {
+ border:1px solid #090;
+}
+.message .input_header {
+ color:blue;
+ float:left;
+ width:10%;
+}
+.message .input {
+ float:left;
+ width:90%;
+ margin-bottom:4px;
+}
+.inputText {
+ font-size:10pt;
+ width:100%;
+ padding:0;
+ border:none;
+ outline:none;
+}
+.message .output_header {
+ color:red;
+ float:left;
+ width:10%;
+}
+.message .output {
+ float:left;
+ width:90%;
+ height:0px;
+}
+
+
+/*
+ * Tab completer
+ */
+.completer {
+ position:absolute;
+ background:white;
+ border:1px solid black;
+ border-bottom-left-radius:3px;
+ border-bottom-right-radius:3px;
+ -moz-border-radius-bottomleft:3px;
+ -moz-border-radius-bottomright:3px;
+ font-size:8pt;
+}
+.completer .column {
+ float:left;
+ margin:0px 3px;
+}
+.completer .selection {
+ padding:2px;
+ cursor:pointer;
+}
+.completer .selected {
+ background-color:#CCC;
+}
Binary file not shown.
@@ -0,0 +1,100 @@
+""" A minimal application using the IPython web notebook frontend.
+"""
+
+#-----------------------------------------------------------------------------
+# Imports
+#-----------------------------------------------------------------------------
+
+# Local imports
+import os
+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 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(("", 8080), IPyHttpHandler)
+ server.serve_forever()
+
+if __name__ == '__main__':
+ #FIXME: Ctrl-C is not trapped by this try-except, gets lost somewhere in kernel manager
+ try:
+ import threading
+ def defer():
+ """Defer the startup of the browser
+ """
+ import time
+ time.sleep(2)
+ import webbrowser
+ webbrowser.open("http://localhost:8080/notebook")
+ #I need threading here because the main function blocks -- better ideas?
+ threading.Thread(target=defer).start()
+ main()
+ except KeyboardInterrupt:
+ pass
Oops, something went wrong.