Permalink
Browse files

More work on interacts using ipython message handlers

  • Loading branch information...
1 parent 2863a9f commit 07213aab5672b94273d6bc303c3aea174ae90d40 Jason Grout committed Dec 18, 2012
Showing with 119 additions and 3 deletions.
  1. +104 −1 receiver.py
  2. +15 −2 static/compute_server.js
View
105 receiver.py
@@ -99,14 +99,117 @@ def new_files(root='./'):
ip.payload_manager.write_payload({"new_files": new_files})
return ''
_sage_.new_files = new_files
+
+ def handler_wrapper(key, handler):
+ """
+ On the one hand, it makes a lot of sense to just call
+ run_cell with store_history=False and silent=True. Then
+ the message will be transformed, all of the necessary
+ error handling will be put in place, etc. However, it
+ adds quite a bit of overhead, with the pre_run_code bit,
+ the user_variables bit, etc. Also, if the user has handed
+ you a function, you actually want to call that function,
+ instead of whatever has that name currently (i.e., you
+ want to use the actual function and closure, not just
+ convert things back to strings again). Even building up
+ an AST right away calls the function name rather than the
+ actual function. (what I wouldn't give for Lisp macros
+ right now! :).
+
+ On the other hand, if we just literally store the function
+ and call the function, then it's hard to run in the user
+ namespace. How do you exec in a namespace, but use an
+ actual function object rather than trying to find the
+ string. Oh, I guess you can just assign the function to
+ some storage dictionary and use *that* string, and hope
+ the user doesn't change that dictionary. In a sense,
+ that's doing a gensym.
+
+ The last is probably the best approach. Use that and
+ run_code, though we should time things to see how much
+ overhead is introduced, or at least provide an option for
+ running a minimal version of the code.
+
+ Pursuant to this, we should probably remove the ident and
+ stream options, and just provide the actual message to the
+ handler. The handler can return a content and metadata
+ dictionary that will automatically be sent in a
+ key+'_reply' message, or raise an error that will be sent
+ in that status message.
+
+ So, still to do: either make the execute_request handler a
+ subcase of this, or abstract out some of the things done
+ in the handler into maybe a context manager so that the
+ things like sending a kernel busy message are shared.
+
+ Discuss namespaces and things for message ids. I think
+ it's fine to request that a module that is adding handler
+ functions use a message type that reflects the module
+ name, or in some way reflects the project (e.g.,
+ 'sagenb.interact.update')
+
+ Also, should these requests be broadcast out to other
+ clients? I think not, but double-check this.
+
+ Provide an option to just run the code with minimal
+ changes (i.e., no input splitting). This provides fast
+ execution.
+
+ """
+
+ kernel = ka.kernel
+ from functools import wraps
+ @wraps(handler)
+ def f(stream, ident, parent, *args, **kwargs):
+ kernel._publish_status(u'busy', parent)
+ md = kernel._make_metadata(parent['metadata'])
+ # Set the parent message of the display hook and out streams.
+ kernel.shell.displayhook.set_parent(parent)
+ kernel.shell.display_pub.set_parent(parent)
+ kernel.shell.data_pub.set_parent(parent)
+ sys.stdout.set_parent(parent)
+ sys.stderr.set_parent(parent)
+ reply_content = {}
+ try:
+ reply_content[u'result'] = handler(stream, ident, parent, *args, **kwargs)
+ except:
+ status = u'error'
+ etype, evalue, tb = sys.exc_info()
+ import traceback
+ tb_list = traceback.format_exception(etype, evalue, tb)
+ reply_content.update(kernel.shell._showtraceback(etype, evalue, tb_list))
+ else:
+ status = u'ok'
+ reply_content[u'status'] = status
+ sys.stdout.flush()
+ sys.stderr.flush()
+ if kernel._execute_sleep:
+ import time
+ time.sleep(kernel._execute_sleep)
+
+ from IPython.utils.jsonutil import json_clean
+ reply_content = json_clean(reply_content)
+
+ md['status'] = reply_content['status']
+ if reply_content['status'] == 'error' and \
+ reply_content['ename'] == 'UnmetDependency':
+ md['dependencies_met'] = False
+ reply_msg = kernel.session.send(stream, key+u'_reply',
+ reply_content, parent, metadata=md,
+ ident=ident)
+ kernel.log.debug("%s", reply_msg)
+
+ kernel._publish_status(u'idle', parent)
+ return f
def register_handler(key, handler):
msg_types = set([ 'execute_request', 'complete_request',
'object_info_request', 'history_request',
'connect_request', 'shutdown_request',
'apply_request',
])
+
if key not in msg_types:
- ka.kernel.shell_handlers[key] = handler
+ ka.kernel.shell_handlers[key] = handler_wrapper(key, handler)
_sage_.register_handler = register_handler
def send_message(stream, msg_type, content, parent, **kwargs):
ka.kernel.session.send(stream, msg_type, content=content, parent=parent, **kwargs)
View
17 static/compute_server.js
@@ -239,12 +239,18 @@ sagecell.Session.prototype.output = function(html, block_id, create) {
return out;
};
+sagecell.Session.prototype.handle_message_reply = function(msg) {
+}
+
+
sagecell.Session.prototype.handle_execute_reply = function(msg) {
sagecell.log('reply walltime: '+this.timer() + " ms");
- if(msg.status==="error") {
+ /* This would give two error messages (since a pyerr should have already come)
+ if(msg.status==="error") {
this.output('<pre class="sagecell_pyerr"></pre>',null)
.html(IPython.utils.fixConsole(msg.traceback.join("\n")));
}
+ */
var payload = msg.payload[0];
if (payload && payload.new_files){
var files = payload.new_files;
@@ -268,6 +274,9 @@ sagecell.Session.prototype.handle_execute_reply = function(msg) {
}
sagecell.Session.prototype.handle_output = function (msg_type, content, metadata) {
+ console.log('handling output');
+ console.log(msg_type);
+ console.log(content);
var block_id = metadata.interact_id || null;
// Handle each stream type. This should probably be separated out into different functions.
switch (msg_type) {
@@ -350,7 +359,11 @@ sagecell.InteractCell.prototype.bindChange = function () {
}
}
that.session.replace_output[that.interact_id] = true;
- that.session.send_message('sagenb.interact.update_interact', msg_dict);
+ var callbacks = {"output": $.proxy(that.session.handle_output, that.session),
+ "sagenb.interact.update_interact_reply": $.proxy(that.session.handle_message_reply, that.session)};
+
+ that.session.send_message('sagenb.interact.update_interact', msg_dict,
+ callbacks);
}
};
for (var name in this.controls) {

0 comments on commit 07213aa

Please sign in to comment.