Skip to content
This repository

Html notebook #179

Closed
wants to merge 36 commits into from

7 participants

James Gao markvoorhies Fernando Perez Carlos Cordoba Brian E. Granger Ivan Savov Jason Grout
James Gao

Hi everyone,
I've coded up a preliminary Javascript-based frontend for the new zmq kernel. Fernando helped me get my git set up for this, so a big kudos to him!

I'm new at python setup scripts, but if I did this correctly, you can run this branch with ipython-http. If this did not work, you can try running ipythonhttp.py inside IPython/frontend/html/. As a note, you cannot run the file from the same directory, due to how it extracts the current directory name. Go to IPython/frontend, then run python html/ipythonhttp.py --pylab inline.

As an overview of the architecture:
The HTTP server is just a default BaseHTTPServer, where the BaseHTTPHandler directly hooks into a module-level CometManager object. This object keeps track of any messages the kernel emits, and multiplexes the messages over HTTP to any all clients. The design of IPython.frontend.html.kernelmanager is nearly a direct copy from the qt version. ipythonhttp.py is also a direct copy from the qt.console.ipythonqt. In other words, it is possible to connect the http server to an existing kernel using the exact same commands as in ipython-qtconsole

On the client side, Jquery was used to build most of the javascript objects. Unfortunately, the javascript is poorly tested, and may be fragile. Most of the XREQ and PUB features are supported. Messages on the PUB socket will be displayed by all web clients. This is accomplished with the "Comet" design pattern, where the client opens and maintains a long-living connection to the server in the form of a GET request. The server waits on the kernel for a message, which it immediately sends along. Anything on the PUB socket is transmitted via GET requests, and anything over XREQ is over POST requests. Multi-line inputs are supported with ctrl-enter, with shift-enter to submit. Inline SVGs are supported for pylab inline, and a notebook-like interface for interaction. Tab completion will open a selection window for candidates.

Some known issues:
Major security vulnerability: direct shell access on the internet. Make sure your firewall is secure!
There are no tests for the javascript code
If multiple web clients are connected, message numbers might be messed up
Sometimes, the entry message may disappear without adding another
Some responding "output" messages may get swallowed by the javascript manager (let me know if you can find a reproducible case)

Please give me some feedback on this code!

and others added some commits October 11, 2010
James Gao First commit for basehttpserver -- mostly a copy from qtkernelmanager 7fee9ed
James Gao Earliest barebones dumping of the sub socket to http, will improve soon 7100286
James Gao Add logic to serve up normal files, useful for javascript files edd7029
xreq_channel now working with the http channel! Need to make sure tha…
…t code really is as simple as I've made it...
d18541a
Probably best to not commit jquery... Let's link it from googlecode 9dce0dc
James Gao Add some javascript to make terminal listing pretty, specifically, to…
… turn escaped terminal commands (colors) into html. Fancy regexps!
d5a3490
Finally making better javascript tracking, looks more like ipython now ddffe2b
James Gao Partially fix "fixConsole" function which was breaking on some outputs 30d7333
James Gao Fix the kernelmanager so that running it from arbitrary paths work 7fe119b
James Gao Forgot to add basepath to the os.path.exists, otherwise it only sends
404 for the JS files
3001fb5
Add support for inline svg dumping. Now pylab works! 8b70aad
James Gao Argh, that was a painful commit, making full message tracking so hist…
…ory / notebook behavior is supported
6abdb49
James Gao Activated "uparrow" history browsing! simple! a23e40e
James Gao Split the JS functions into logical groupings, fix some more bugs 990adfc
James Gao Fix up a bunch of movement bugs, allow clickable activate, properly m…
…anage ondeck message
6f1c4f5
James Gao Still trying to fix message tracking bugs. SVG works again... 97c2417
James Gao Massive overhaul of the message tracking interface, bugs remain... dc0be4b
James Gao Fixed the extra pyin connect message bug by deleting all empty messag…
…es -- also useful for cleaning up messages
e030733
James Gao Allow payload to display text, partial support for object inspection.…
… I need to enable the full object_info_request before this is satisfying
974e5d2
James Gao Some improved styling, added preliminary history support. Pity the ba…
…ckend breaks with history still...
c5168d5
James Gao Implement a full autocomplete browser. Still need to juggle some more…
… events to make it more consistent
586a263
James Gao Add some history support, still need main bug fixed. Tweak scrolling a0cf5f8
James Gao Multiline entry support cce11cd
James Gao Goddamn message tracking still being impossible to deal with, need to
queue up messages on the comet side to make execute ok
310913d
James Gao Tweak the styling in the history, delete extraneous print in kernelma…
…nager
efbcf10
James Gao Tweak some styles, fix tab complete with other delimiters c7d81fc
James Gao Managed to fix the indenter, go figure... b8c76d1
James Gao I think I may have fixed the message tracking bugs. Now, all messages…
… will actually display! Also, tweaked the styling
2117482
James Gao Fix a keyerror bug in the kernelmanager, prepare the code for pull re…
…quest
fb474f4
James Gao oops, stupid tabs... 77299d3
markvoorhies

Wow, insanely cool!

I had to jump through a few hoops to make it work, perhaps because I'm not doing a system level install.
Here's what I did:

Patch setupbase.py to install frontend.html:
mvoorhie@virgil:~/Cpackages/iPython/ipython$ git diff
diff --git a/setupbase.py b/setupbase.py
index 9d4215d..2530623 100644
--- a/setupbase.py
+++ b/setupbase.py
@@ -111,7 +111,8 @@ def find_packages():
add_package(packages, 'frontend')
add_package(packages, 'frontend.qt')
add_package(packages, 'frontend.qt.console', tests=True)

  • add_package(packages, 'frontend.terminal', tests=True)
  • add_package(packages, 'frontend.terminal', tests=True)
  • add_package(packages, 'frontend.html') add_package(packages, 'kernel', config=False, tests=True, scripts=True) add_package(packages, 'kernel.core', config=False, tests=True) add_package(packages, 'lib', tests=True)

Run setup.py for local install (zmq and pyzmq are in the same location):
python setup.py install --prefix=/home/mvoorhie/Cpackages/iPython/local/

Symlink the static web bits:
cd /home/mvoorhie/Cpackages/iPython/local/bin/
ln --symbolic /home/mvoorhie/Cpackages/iPython/ipython/IPython/frontend/html/css/ .
ln --symbolic /home/mvoorhie/Cpackages/iPython/ipython/IPython/frontend/html/js/ .
ln --symbolic /home/mvoorhie/Cpackages/iPython/ipython/IPython/frontend/html/notebook.html .
cd -

Invoke:
export PYTHONPATH="$PYTHONPATH:/home/mvoorhie/Cpackages/iPython/local/lib/python2.6/site-packages/:/home/mvoorhie/Cpackages/iPython/local/lib/"
export LD_LIBRARY_PATH=/home/mvoorhie/Cpackages/iPython/local/lib/
export PATH="$PATH:/home/mvoorhie/Cpackages/iPython/local/bin/"
ipython-http --pylab inline

Looks beautiful in Firefox 3.6.10 and Konqueror/Webkit 4.4.2 simultaneosly =)

markvoorhies

Oops, forgot to preview my last comment. The patch should look like this:

mvoorhie@virgil:~/Cpackages/iPython/ipython$ git diff
diff --git a/setupbase.py b/setupbase.py
index 9d4215d..2530623 100644
--- a/setupbase.py
+++ b/setupbase.py
@@ -111,7 +111,8 @@ def find_packages():
     add_package(packages, 'frontend')
     add_package(packages, 'frontend.qt')
     add_package(packages, 'frontend.qt.console', tests=True)
-    add_package(packages, 'frontend.terminal', tests=True)    
+    add_package(packages, 'frontend.terminal', tests=True)
+    add_package(packages, 'frontend.html')
     add_package(packages, 'kernel', config=False, tests=True, scripts=True)
     add_package(packages, 'kernel.core', config=False, tests=True)
     add_package(packages, 'lib', tests=True)
Fernando Perez
Owner

James, Mark's comment is the patch we talked about today right before you left. It would be great if you could apply this little fix soon, to make it easier for others to test your code (which is really, really cool :)

James Gao

Thanks Mark, I've added your patch. As for linking the html files, I'm not sure what's the best solution to this. Currently, the html, css, and javascript files are in the same directory as the main module. The module sets its own basepath using its main.file, and looks for the path from there. Evidently, this does not work with the install script. Since I'm not familiar with the distutils and setup, can someone suggest the best way to make /usr/local/bin/ipython-http work directly?

Fernando Perez
Owner

I can't help right now (leaving town shortly) but if you don't get help separately, I'll explain how to do it after the weekend...

Carlos Cordoba

I think the page should be named "IPython session" instead of "Ajax Test". It could use the project's logo as favicon too, or maybe just [I]:

Besides, I think the frontend should be named ipython-webnb instead of ipython-http, per the discussion that took place on the dev list a few days ago.

Fernando Perez
Owner

Hey James,

this looks great, and I really want to get it merged in sooner rather than later, so you don't have to work in isolation for very long.

A few small notes from random testing:

  • you may want to move the code that opens the web browser to happen a little bit later, once you know the kernel is actively listening. Right now by default it tries to open the browser too early, and the user gets an error page. A few seconds later reloading works fine, but it would be better not to show the error page in the first place.

  • Similarly, you should trap KeyboardInterrupt in the main loop, and use that to shut down cleanly. It's a good way to stop the service from a terminal, and likely to be used by regular users. Just catch the KeyboardInterrupt exception, use that as your exit request, and do any shutdown operations you need.

  • You should add the standard copyright notice

#-----------------------------------------------------------------------------
# Copyright (c) 2010, IPython Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file COPYING.txt, distributed with this software.
#-----------------------------------------------------------------------------

in the files as you have a chance. It's best that these are carried regularly.

  • Could you also add at the top of the JS files a comment block indicating the intent of the file? I know JS doesn't have docstrings in the Python sense, but a comment block similar to the top-level docstrings will still help. And in general, try to err on the side of verbosity with comments on JS code: most of us in the team know a lot more python than JS, so having the JS code well commented will help us a lot when we dig into it.

  • In html/kernelmanager:

    • Threading module is imported but not needed (you can catch these things by using pyflakes regularly, let me know if you need a hand setting it up; it's a great habit to get into).

    • Try to organize the file a little better overall: imports at the top organized by section, classes, functions, etc. I've added a sample template you can use for reference, to the docs:

http://ipython.scipy.org/doc/nightly/html/development/coding_guide.html#new-files

  • Could you add a reference to the Comet design pattern? I'm not familiar with it, and a URL in the code would help to make sure we look up the same thing you had in mind.

  • Global objects should be clearly declared in a globals section at the top. If some globals are needed that are instances of classes defined in the file itself, then put them all together at the bottom so they stand out clearly (they get easily lost otherwise).

  • Unit tests? Not everything here is easy to auto-test, but objects like the CometManger are quite easy to have tests written for them. Look in the tests/ directories in IPython for test examples, or let me know if you want a hand getting going with this and we can work together. We really, really need to have as tight of a test harness as possible for these codes right off the start.

  • Also add at least some basic docstrings for the various methods, so that other developers know what each of them is for. For user-facing code we want very well documented/formatted docstrings (http://ipython.scipy.org/doc/nightly/html/development/doc_guide.html#docstring-format) but we're not quite that exacting for developer-only code. Still, a basic docstring and parameter explanation goes a long way towards helping everyone be able to work with the code later on.

  • Similarly, could you drop a little reST file in the docs with a basic description of the architecture? Even the text from your pull request (above) would be a great start. In docs/source, add a directory called frontend/ and put in there a file called webnb.txt (to match the top-level script name).

  • Are the various Http*socketChannel classes still missing some implementation work? I see a few 'pass' in there. Do you think you can finish them off before merging? If not that's OK, just mark clearly with #FIXME roughly what's missing, so we know when looking again.

Note: if you need icons, http://ipython.scipy.org/ipython-icons/ has our current set of icons in png format. We can ask Min for the original svg later.

Summary

This is fantastic work. I'm very, very excited about this, and I hope we can get it merged very soon. Let me know if you want a hand with any of the review items above, and we'll get this in. I'll make a note on the list asking for further review on more web-specific stuff, as I know very little about those architectures.

James Gao

Hi Fernando,
Thanks for your comments! I did some code reshuffling and made everything more clear. I also tightened up message handling on the javascript side, so strange cases like this:

sys.stdout.write("Hello ")
sys.stdout.flush()
time.sleep(2)
sys.stdout.write("World")
sys.stdout.flush()

actually work correctly now. As a side effect (feature!) of this reorganization, now graphs are pushed to all web clients, regardless of who made the request. I'll try to get the documentation and unit-testing up to par over the next few days, although I need to get up to speed on Sphinx.

Fernando Perez
Owner

Hey James,

thanks, this looks like good progress. One minor point I failed to notice yesterday: we're trying to use pep-8 names by default, which means calling module files with lowercase only: CometManager.py -> cometmanager.py (I'd actually just call it 'comet.py' for short).

the rest of the changes in the last two commits look good. Ping us here when you've completed things and we'll go over it again.

Brian E. Granger
Owner

This is my first round of comment, mostly focusing on the UI.

  • Use a monospaced font for all code and the In/Out prompts. I noticed that the font while you are entering code is not monospaced, but after you are done editing, it becomes monospaced.

  • Less horizontal space between the end of the prompts and the beginning of code and payloads. There is currently about ~10 chars worth of it.

  • The first time you press ctrl-return, it pauses for a second and put an extra space before the next line of input. From then on it doesn't pause to put the extra line in.

  • Use Return for newline, not for code execution. Use shift-return for code execution.

  • The little dot should probably be red when the kernel is busy. I like the green dot for idle though.

  • How do you delete a cell? Insert a new one between existing ones? Reorder cells?

  • I think we should split the Ui into 2 tabs, one tab for the notebook view, which is like the main pane you currently have and another for "timeline" or history view, which would basically be your history side pane. The current history pane will be too small for larger code blocks. This would allow you to make the timeline view look nicer and include plots, etc. It will also keep the main pane less distracting.

  • Should be some sort of title or outer framing to indicate what this is like "IPython Notebook" and maybe even include the ip/port and a link you can email to others.

  • Probably want the ability to collapse large cells and hide the output.

  • When the kernel is busy, we should indicate (with visual highlights) which cell is running.

More comments on the way about the actual code and architecture. Great work though!

James Gao

Thanks for the comments. A lot of these features are planned for a full notebook interface, which would allow cell shuffling, grouping, etc. That's probably a big enough merge by itself to be a separate pull request. Let's look through this list:

1) Good call on the monospace font -- the styles are not very tight yet, I have to review what looks best.
2-3) This sounds like an animation bug, can you send a screenshot? Which browser / version are you running?
4) Currently, return on a single line will execute, whereas shift-return is required for multi-line input. It's an easy thing to change -- what's the best default?
5) The dot becomes red when the kernel is "dead", when the COMET requests have ceased.
6) Deleting a cell is currently accomplished by erasing the entire input, and return. I'm planning to add a context menu that would accomplish this.
7) UI tabs: I like this idea! I'll probably implement it for a more full fledged notebook interface
8-9) These features are mostly planned for the full notebook interface
10) Another great idea! I'll try to add that tonight maybe?

Fernando Perez
Owner

4) You almost have the interface that the Qt console has, which is that return executes complete single-line statements, but does newline for incomplete ones (colon at end of line, open quotes, open parens, etc). However, it's very hard to do that in JS, because you need a real python compiler to reliably detect incomplete python statements (that's what we use, the actual compiler).

So I suggest in the NB to simply have, without any smarts:

  • Return: newline always

  • Shift-return: unconditional code execution.

Later, I'd use Ctrl-Return to split up a cell in two, which can be very handy.

Brian E. Granger
Owner

1) Great
2-3) This sounds like an animation bug, can you send a screenshot? Which browser / version are you running?

I get the bug in Chrome 7. I will post a screenshot to the list. Also, get a minor issue at the same place with Firefox.

4) Currently, return on a single line will execute, whereas shift-return is required for multi-line input. It's an easy thing to change -- what's the best default?

I agree with Fernando about Return = newline, Shift-Return=execute code.

5) The dot becomes red when the kernel is "dead", when the COMET requests have ceased.

Ahh, maybe something like this:

  • Green dot = idle
  • Red dot = busy
  • Invert the entire div to red put "Dead" in while text = dead

6) Deleting a cell is currently accomplished by erasing the entire input, and return. I'm planning to add a context menu that would accomplish this.

We should have a keyboard stroke dedicated to this...ctrl-D? command-D on Mac?

7) UI tabs: I like this idea! I'll probably implement it for a more full fledged notebook interface
8-9) These features are mostly planned for the full notebook interface

Great

10) Another great idea! I'll try to add that tonight maybe?

Awesome.

Brian E. Granger
Owner

OK, some additional issue:

1) When I tab complete, I get:

File "/Users/bgranger/Documents/Computation/IPython/code/ipython/IPython/frontend/html/kernelmanager.py", line 95, in do_POST
resp = manager.complete(data["code"].value, data["pos"].value)
File "/Users/bgranger/Documents/Computation/IPython/code/ipython/IPython/frontend/html/CometManager.py", line 62, in complete
chunk = re.split('\s|(|=|;', code[:int(pos)])[-1]
NameError: global name 're' is not defined

Looks like simply an import error.

2) When it starts up it has two cells showing In[0] and In[1], both blank.

Fernando Perez
Owner

James, Brian and I had a long talk about the web architecture and things him and Min are doing on the pyzmq side. The short of it is that in a little while, it will likely be possible to refactor some of your http code to run on top of the Tornado web server (imported from pyzmq). This will give us ssl support, a better async model and better scalability.

This is just to give you a heads-up not to worry too much about major work on the http serving part of your code right now. I think if you have a chance to wrap up the various items pointed in this review, we're pretty much ready to merge once those are fixed.

Once merged, we can then continue to build on it and later in the month revisit the http serving part of things. But your code as it is, is a great starting point to build on.

Fernando Perez
Owner

James, I noticed one more problem: if you actually install your branch, then it doesn't work right. I did an install to a temporary throw-away directory, and in the browser I get:

XML Parsing Error: no element found
Location: http://localhost:8080/notebook
Line Number 1, Column 1:

while the terminal shows:

----------------------------------------
localhost.localdomain - - [29/Oct/2010 21:16:31] "GET /notebook HTTP/1.1" 200 -
----------------------------------------
Exception happened during processing of request from ('127.0.0.1', 57620)
Traceback (most recent call last):
  File "/usr/lib/python2.6/SocketServer.py", line 560, in process_request_thread
    self.finish_request(request, client_address)
  File "/usr/lib/python2.6/SocketServer.py", line 322, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "/usr/lib/python2.6/SocketServer.py", line 617, in __init__
    self.handle()
  File "/usr/lib/python2.6/BaseHTTPServer.py", line 329, in handle
    self.handle_one_request()
  File "/usr/lib/python2.6/BaseHTTPServer.py", line 323, in handle_one_request
    method()
  File "/home/fperez/tmp/junk/lib/python2.6/site-packages/IPython/frontend/html/kernelmanager.py", line 60, in do_GET
    page_text = Template(open(basepath + "notebook.html").read())
IOError: [Errno 2] No such file or directory: '/home/fperez/tmp/junk/lib/python2.6/site-packages/IPython/frontend/html/notebook.html'
----------------------------------------

You may need to play with the data_files and/or package_data (I can't remember precisely which right now, have a look at the distutils docs for both of these flags), because I think the problem is that the js/css code isn't getting put in the installed version.

With new code like this, it's always a good idea to test it both from a source tree and from a temporary installation.

Fernando Perez
Owner

Hey James,

Satra made a good suggestion in general, but the html notebook is probably the most critical place to put it in. How about we add a warning at the top of the opening page for now that reads:

Since the ZMQ code currently has no security, listening on an external-facing IP is dangerous. 
You are giving any computer that can see you on the network the ability to issue arbitrary shell commands as you on your machine. 
Be very careful with this.

Hopefully we can remove it soon, but it's probably a good idea to have in there for now.

Ivan Savov

How does the sage notebook handle security?
http://nb.sagemath.org/

very cool!

Jason Grout

What is the status of this pull request? It seems very relevant to our new rewrite of the Sage notebook.

James Gao

Unfortunately, I haven't had enough time to develop this further. I would like to get back into it soon though, so please keep watching. I'm planning to move over to tornado as per the suggestions above.

Jason Grout

Several students and I are working on a "single cell Sage notebook" which shares a lot of similarities with your project. At the recent Sage Days 29, I talked a lot with Fernando and others about using the ipython protocol to implement our project. We are implementing our project using uwsgi and flask, so we wouldn't be using websockets, but instead use a database as a huge cache of the requests between the client and server.

Anyways, I'm following this and may use or extend the javascript code here as part of our implementation of the messaging spec. We will probably work on this pretty heavily starting in May.

Brian E. Granger ellisonbg closed this March 31, 2011
Brian E. Granger
Owner

The html notebook work is being continued in the htmlnotebook branch here:

https://github.com/ipython/ipython/tree/htmlnotebook

Jason Grout

Great; thanks!

Damián Avila damianavila referenced this pull request from a commit July 30, 2013
Commit has since been removed from the repository and is no longer available.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 36 unique commits by 2 authors.

Oct 21, 2010
James Gao First commit for basehttpserver -- mostly a copy from qtkernelmanager 7fee9ed
James Gao Earliest barebones dumping of the sub socket to http, will improve soon 7100286
James Gao Add logic to serve up normal files, useful for javascript files edd7029
xreq_channel now working with the http channel! Need to make sure tha…
…t code really is as simple as I've made it...
d18541a
Probably best to not commit jquery... Let's link it from googlecode 9dce0dc
James Gao Add some javascript to make terminal listing pretty, specifically, to…
… turn escaped terminal commands (colors) into html. Fancy regexps!
d5a3490
Finally making better javascript tracking, looks more like ipython now ddffe2b
James Gao Partially fix "fixConsole" function which was breaking on some outputs 30d7333
James Gao Fix the kernelmanager so that running it from arbitrary paths work 7fe119b
James Gao Forgot to add basepath to the os.path.exists, otherwise it only sends
404 for the JS files
3001fb5
Add support for inline svg dumping. Now pylab works! 8b70aad
James Gao Argh, that was a painful commit, making full message tracking so hist…
…ory / notebook behavior is supported
6abdb49
James Gao Activated "uparrow" history browsing! simple! a23e40e
James Gao Split the JS functions into logical groupings, fix some more bugs 990adfc
James Gao Fix up a bunch of movement bugs, allow clickable activate, properly m…
…anage ondeck message
6f1c4f5
James Gao Still trying to fix message tracking bugs. SVG works again... 97c2417
James Gao Massive overhaul of the message tracking interface, bugs remain... dc0be4b
James Gao Fixed the extra pyin connect message bug by deleting all empty messag…
…es -- also useful for cleaning up messages
e030733
James Gao Allow payload to display text, partial support for object inspection.…
… I need to enable the full object_info_request before this is satisfying
974e5d2
James Gao Some improved styling, added preliminary history support. Pity the ba…
…ckend breaks with history still...
c5168d5
James Gao Implement a full autocomplete browser. Still need to juggle some more…
… events to make it more consistent
586a263
James Gao Add some history support, still need main bug fixed. Tweak scrolling a0cf5f8
James Gao Multiline entry support cce11cd
James Gao Goddamn message tracking still being impossible to deal with, need to
queue up messages on the comet side to make execute ok
310913d
James Gao Tweak the styling in the history, delete extraneous print in kernelma…
…nager
efbcf10
James Gao Tweak some styles, fix tab complete with other delimiters c7d81fc
James Gao Managed to fix the indenter, go figure... b8c76d1
James Gao I think I may have fixed the message tracking bugs. Now, all messages…
… will actually display! Also, tweaked the styling
2117482
James Gao Fix a keyerror bug in the kernelmanager, prepare the code for pull re…
…quest
fb474f4
James Gao oops, stupid tabs... 77299d3
Oct 22, 2010
James Gao Added diff from markvoorhies... linking webpages still not working! 516ff28
James Gao Change __main__.__file__ to just __file__, now ipython-http knows whe…
…re to get html and js files
9a75ab3
Oct 27, 2010
James Gao Changed name of ipython-http per ccordoba12's suggestion, fixed a cli…
…ck bug in messages, and added a favicon
c02891f
James Gao Starting work on the new message tracking design, better support for …
…sleep;print;sleep pattern
ad87ff3
Oct 28, 2010
James Gao Rewrote backend js for new message tracking, works vastly better now a1ed6cd
James Gao Do some code reorganization per Fernando's comments f476146
Something went wrong with that request. Please try again.