Merge master

Gordon Ball 12 years ago
commit 08895738f2

@ -235,12 +235,13 @@ class IPythonHandler(AuthenticatedHandler):
raise web.HTTPError(400, u'Invalid JSON in body of request')
return model
def get_error_html(self, status_code, **kwargs):
def write_error(self, status_code, **kwargs):
"""render custom error pages"""
exception = kwargs.get('exception')
exc_info = kwargs.get('exc_info')
message = ''
status_message = responses.get(status_code, 'Unknown HTTP Error')
if exception:
if exc_info:
exception = exc_info[1]
# get the custom message, if defined
try:
message = exception.log_message % exception.args
@ -260,13 +261,16 @@ class IPythonHandler(AuthenticatedHandler):
exception=exception,
)
self.set_header('Content-Type', 'text/html')
# render the template
try:
html = self.render_template('%s.html' % status_code, **ns)
except TemplateNotFound:
self.log.debug("No template for %d", status_code)
html = self.render_template('error.html', **ns)
return html
self.write(html)
class Template404(IPythonHandler):

@ -514,15 +514,20 @@ define([
}
};
var ajax_error_msg = function (jqXHR) {
// Return a JSON error message if there is one,
// otherwise the basic HTTP status text.
if (jqXHR.responseJSON && jqXHR.responseJSON.message) {
return jqXHR.responseJSON.message;
} else {
return jqXHR.statusText;
}
}
var log_ajax_error = function (jqXHR, status, error) {
// log ajax failures with informative messages
var msg = "API request failed (" + jqXHR.status + "): ";
console.log(jqXHR);
if (jqXHR.responseJSON && jqXHR.responseJSON.message) {
msg += jqXHR.responseJSON.message;
} else {
msg += jqXHR.statusText;
}
msg += ajax_error_msg(jqXHR);
console.log(msg);
};
@ -547,6 +552,7 @@ define([
platform: platform,
is_or_has : is_or_has,
is_focused : is_focused,
ajax_error_msg : ajax_error_msg,
log_ajax_error : log_ajax_error,
};

@ -2286,13 +2286,14 @@ define([
*/
Notebook.prototype.load_notebook_error = function (xhr, status, error) {
this.events.trigger('notebook_load_failed.Notebook', [xhr, status, error]);
utils.log_ajax_error(xhr, status, error);
var msg;
if (xhr.status === 400) {
msg = error;
msg = escape(utils.ajax_error_msg(xhr));
} else if (xhr.status === 500) {
msg = "An unknown error occurred while loading this notebook. " +
"This version can load notebook formats " +
"v" + this.nbformat + " or earlier.";
"v" + this.nbformat + " or earlier. See the server log for details.";
}
dialog.modal({
notebook: this,
@ -2567,10 +2568,10 @@ define([
* @method delete_checkpoint_error
* @param {jqXHR} xhr jQuery Ajax object
* @param {String} status Description of response status
* @param {String} error_msg HTTP error message
* @param {String} error HTTP error message
*/
Notebook.prototype.delete_checkpoint_error = function (xhr, status, error_msg) {
this.events.trigger('checkpoint_delete_failed.Notebook');
Notebook.prototype.delete_checkpoint_error = function (xhr, status, error) {
this.events.trigger('checkpoint_delete_failed.Notebook', [xhr, status, error]);
};

@ -62,22 +62,35 @@ define([
// handle in the vertical slider is always
// consistent.
var orientation = this.model.get('orientation');
var value = this.model.get('min');
var min = this.model.get('min');
var max = this.model.get('max');
if (this.model.get('range')) {
this.$slider.slider('option', 'values', [value, value]);
this.$slider.slider('option', 'values', [min, min]);
} else {
this.$slider.slider('option', 'value', value);
this.$slider.slider('option', 'value', min);
}
this.$slider.slider('option', 'orientation', orientation);
value = this.model.get('value');
var value = this.model.get('value');
if (this.model.get('range')) {
// values for the range case are validated python-side in
// _Bounded{Int,Float}RangeWidget._validate
this.$slider.slider('option', 'values', value);
this.$readout.text(value.join("-"));
} else {
if(value > max) {
value = max;
}
else if(value < min){
value = min;
}
this.$slider.slider('option', 'value', value);
this.$readout.text(value);
}
if(this.model.get('value')!=value) {
this.model.set('value', value, {updated_view: this});
this.touch();
}
// Use the right CSS classes for vertical & horizontal sliders
if (orientation=='vertical') {

@ -80,5 +80,5 @@ default_handlers = [
(r"/tree%s" % notebook_path_regex, TreeHandler),
(r"/tree%s" % path_regex, TreeHandler),
(r"/tree", TreeHandler),
(r"", TreeRedirectHandler),
(r"/?", TreeRedirectHandler),
]

@ -13,11 +13,12 @@ in the IPython notebook front-end.
# Imports
#-----------------------------------------------------------------------------
from contextlib import contextmanager
import collections
from IPython.core.getipython import get_ipython
from IPython.kernel.comm import Comm
from IPython.config import LoggingConfigurable
from IPython.utils.traitlets import Unicode, Dict, Instance, Bool, List, Tuple, Int
from IPython.utils.traitlets import Unicode, Dict, Instance, Bool, List, Tuple, Int, Set
from IPython.utils.py3compat import string_types
#-----------------------------------------------------------------------------
@ -98,9 +99,9 @@ class Widget(LoggingConfigurable):
#-------------------------------------------------------------------------
_model_name = Unicode('WidgetModel', help="""Name of the backbone model
registered in the front-end to create and sync this widget with.""")
_view_name = Unicode(help="""Default view registered in the front-end
_view_name = Unicode('WidgetView', help="""Default view registered in the front-end
to use to represent the widget.""", sync=True)
_comm = Instance('IPython.kernel.comm.Comm')
comm = Instance('IPython.kernel.comm.Comm')
msg_throttle = Int(3, sync=True, help="""Maximum number of msgs the
front-end can send before receiving an idle msg from the back-end.""")
@ -110,7 +111,8 @@ class Widget(LoggingConfigurable):
return [name for name in self.traits(sync=True)]
_property_lock = Tuple((None, None))
_send_state_lock = Int(0)
_states_to_send = Set(allow_none=False)
_display_callbacks = Instance(CallbackDispatcher, ())
_msg_callbacks = Instance(CallbackDispatcher, ())
@ -119,10 +121,12 @@ class Widget(LoggingConfigurable):
#-------------------------------------------------------------------------
def __init__(self, **kwargs):
"""Public constructor"""
self._model_id = kwargs.pop('model_id', None)
super(Widget, self).__init__(**kwargs)
self.on_trait_change(self._handle_property_changed, self.keys)
Widget._call_widget_constructed(self)
self.open()
def __del__(self):
"""Object disposal"""
@ -132,21 +136,20 @@ class Widget(LoggingConfigurable):
# Properties
#-------------------------------------------------------------------------
@property
def comm(self):
"""Gets the Comm associated with this widget.
If a Comm doesn't exist yet, a Comm will be created automagically."""
if self._comm is None:
# Create a comm.
self._comm = Comm(target_name=self._model_name)
self._comm.on_msg(self._handle_msg)
def open(self):
"""Open a comm to the frontend if one isn't already open."""
if self.comm is None:
if self._model_id is None:
self.comm = Comm(target_name=self._model_name)
self._model_id = self.model_id
else:
self.comm = Comm(target_name=self._model_name, comm_id=self._model_id)
self.comm.on_msg(self._handle_msg)
Widget.widgets[self.model_id] = self
# first update
self.send_state()
return self._comm
@property
def model_id(self):
"""Gets the model id of this widget.
@ -164,22 +167,22 @@ class Widget(LoggingConfigurable):
Closes the underlying comm.
When the comm is closed, all of the widget views are automatically
removed from the front-end."""
if self._comm is not None:
if self.comm is not None:
Widget.widgets.pop(self.model_id, None)
self._comm.close()
self._comm = None
self.comm.close()
self.comm = None
def send_state(self, key=None):
"""Sends the widget state, or a piece of it, to the front-end.
Parameters
----------
key : unicode (optional)
A single property's name to sync with the front-end.
key : unicode, or iterable (optional)
A single property's name or iterable of property names to sync with the front-end.
"""
self._send({
"method" : "update",
"state" : self.get_state()
"state" : self.get_state(key=key)
})
def get_state(self, key=None):
@ -187,10 +190,17 @@ class Widget(LoggingConfigurable):
Parameters
----------
key : unicode (optional)
A single property's name to get.
key : unicode or iterable (optional)
A single property's name or iterable of property names to get.
"""
keys = self.keys if key is None else [key]
if key is None:
keys = self.keys
elif isinstance(key, string_types):
keys = [key]
elif isinstance(key, collections.Iterable):
keys = key
else:
raise ValueError("key must be a string, an iterable of keys, or None")
state = {}
for k in keys:
f = self.trait_metadata(k, 'to_json')
@ -255,10 +265,29 @@ class Widget(LoggingConfigurable):
finally:
self._property_lock = (None, None)
@contextmanager
def hold_sync(self):
"""Hold syncing any state until the context manager is released"""
# We increment a value so that this can be nested. Syncing will happen when
# all levels have been released.
self._send_state_lock += 1
try:
yield
finally:
self._send_state_lock -=1
if self._send_state_lock == 0:
self.send_state(self._states_to_send)
self._states_to_send.clear()
def _should_send_property(self, key, value):
"""Check the property lock (property_lock)"""
return key != self._property_lock[0] or \
value != self._property_lock[1]
if (key == self._property_lock[0] and value == self._property_lock[1]):
return False
elif self._send_state_lock > 0:
self._states_to_send.add(key)
return False
else:
return True
# Event handlers
@_show_traceback
@ -388,7 +417,10 @@ class DOMWidget(Widget):
selector: unicode (optional, kwarg only)
JQuery selector to use to apply the CSS key/value. If no selector
is provided, an empty selector is used. An empty selector makes the
front-end try to apply the css to the top-level element.
front-end try to apply the css to a default element. The default
element is an attribute unique to each view, which is a DOM element
of the view that should be styled with common CSS (see
`$el_to_style` in the Javascript code).
"""
if value is None:
css_dict = dict_or_key

@ -0,0 +1,299 @@
"""Contains eventful dict and list implementations."""
# void function used as a callback placeholder.
def _void(*p, **k): return None
class EventfulDict(dict):
"""Eventful dictionary.
This class inherits from the Python intrinsic dictionary class, dict. It
adds events to the get, set, and del actions and optionally allows you to
intercept and cancel these actions. The eventfulness isn't recursive. In
other words, if you add a dict as a child, the events of that dict won't be
listened to. If you find you need something recursive, listen to the `add`
and `set` methods, and then cancel `dict` values from being set, and instead
set `EventfulDict`s that wrap those `dict`s. Then you can wire the events
to the same handlers if necessary.
See the on_events, on_add, on_set, and on_del methods for registering
event handlers."""
def __init__(self, *args, **kwargs):
"""Public constructor"""
self._add_callback = _void
self._del_callback = _void
self._set_callback = _void
dict.__init__(self, *args, **kwargs)
def on_events(self, add_callback=None, set_callback=None, del_callback=None):
"""Register callbacks for add, set, and del actions.
See the doctstrings for on_(add/set/del) for details about each
callback.
add_callback: [callback = None]
set_callback: [callback = None]
del_callback: [callback = None]"""
self.on_add(add_callback)
self.on_set(set_callback)
self.on_del(del_callback)
def on_add(self, callback):
"""Register a callback for when an item is added to the dict.
Allows the listener to detect when items are added to the dictionary and
optionally cancel the addition.
callback: callable or None
If you want to ignore the addition event, pass None as the callback.
The callback should have a signature of callback(key, value). The
callback should return a boolean True if the additon should be
canceled, False or None otherwise."""
self._add_callback = callback if callable(callback) else _void
def on_del(self, callback):
"""Register a callback for when an item is deleted from the dict.
Allows the listener to detect when items are deleted from the dictionary
and optionally cancel the deletion.
callback: callable or None
If you want to ignore the deletion event, pass None as the callback.
The callback should have a signature of callback(key). The
callback should return a boolean True if the deletion should be
canceled, False or None otherwise."""
self._del_callback = callback if callable(callback) else _void
def on_set(self, callback):
"""Register a callback for when an item is changed in the dict.
Allows the listener to detect when items are changed in the dictionary
and optionally cancel the change.
callback: callable or None
If you want to ignore the change event, pass None as the callback.
The callback should have a signature of callback(key, value). The
callback should return a boolean True if the change should be
canceled, False or None otherwise."""
self._set_callback = callback if callable(callback) else _void
def pop(self, key):
"""Returns the value of an item in the dictionary and then deletes the
item from the dictionary."""
if self._can_del(key):
return dict.pop(self, key)
else:
raise Exception('Cannot `pop`, deletion of key "{}" failed.'.format(key))
def popitem(self):
"""Pop the next key/value pair from the dictionary."""
key = next(iter(self))
return key, self.pop(key)
def update(self, other_dict):
"""Copy the key/value pairs from another dictionary into this dictionary,
overwriting any conflicting keys in this dictionary."""
for (key, value) in other_dict.items():
self[key] = value
def clear(self):
"""Clear the dictionary."""
for key in list(self.keys()):
del self[key]
def __setitem__(self, key, value):
if (key in self and self._can_set(key, value)) or \
(key not in self and self._can_add(key, value)):
return dict.__setitem__(self, key, value)
def __delitem__(self, key):
if self._can_del(key):
return dict.__delitem__(self, key)
def _can_add(self, key, value):
"""Check if the item can be added to the dict."""
return not bool(self._add_callback(key, value))
def _can_del(self, key):
"""Check if the item can be deleted from the dict."""
return not bool(self._del_callback(key))
def _can_set(self, key, value):
"""Check if the item can be changed in the dict."""
return not bool(self._set_callback(key, value))
class EventfulList(list):
"""Eventful list.
This class inherits from the Python intrinsic `list` class. It adds events
that allow you to listen for actions that modify the list. You can
optionally cancel the actions.
See the on_del, on_set, on_insert, on_sort, and on_reverse methods for
registering an event handler.
Some of the method docstrings were taken from the Python documentation at
https://docs.python.org/2/tutorial/datastructures.html"""
def __init__(self, *pargs, **kwargs):
"""Public constructor"""
self._insert_callback = _void
self._set_callback = _void
self._del_callback = _void
self._sort_callback = _void
self._reverse_callback = _void
list.__init__(self, *pargs, **kwargs)
def on_events(self, insert_callback=None, set_callback=None,
del_callback=None, reverse_callback=None, sort_callback=None):
"""Register callbacks for add, set, and del actions.
See the doctstrings for on_(insert/set/del/reverse/sort) for details
about each callback.
insert_callback: [callback = None]
set_callback: [callback = None]
del_callback: [callback = None]
reverse_callback: [callback = None]
sort_callback: [callback = None]"""
self.on_insert(insert_callback)
self.on_set(set_callback)
self.on_del(del_callback)
self.on_reverse(reverse_callback)
self.on_sort(sort_callback)
def on_insert(self, callback):
"""Register a callback for when an item is inserted into the list.
Allows the listener to detect when items are inserted into the list and
optionally cancel the insertion.
callback: callable or None
If you want to ignore the insertion event, pass None as the callback.
The callback should have a signature of callback(index, value). The
callback should return a boolean True if the insertion should be
canceled, False or None otherwise."""
self._insert_callback = callback if callable(callback) else _void
def on_del(self, callback):
"""Register a callback for item deletion.
Allows the listener to detect when items are deleted from the list and
optionally cancel the deletion.
callback: callable or None
If you want to ignore the deletion event, pass None as the callback.
The callback should have a signature of callback(index). The
callback should return a boolean True if the deletion should be
canceled, False or None otherwise."""
self._del_callback = callback if callable(callback) else _void
def on_set(self, callback):
"""Register a callback for items are set.
Allows the listener to detect when items are set and optionally cancel
the setting. Note, `set` is also called when one or more items are
added to the end of the list.
callback: callable or None
If you want to ignore the set event, pass None as the callback.
The callback should have a signature of callback(index, value). The
callback should return a boolean True if the set should be
canceled, False or None otherwise."""
self._set_callback = callback if callable(callback) else _void
def on_reverse(self, callback):
"""Register a callback for list reversal.
callback: callable or None
If you want to ignore the reverse event, pass None as the callback.
The callback should have a signature of callback(). The
callback should return a boolean True if the reverse should be
canceled, False or None otherwise."""
self._reverse_callback = callback if callable(callback) else _void
def on_sort(self, callback):
"""Register a callback for sortting of the list.
callback: callable or None
If you want to ignore the sort event, pass None as the callback.
The callback signature should match that of Python list's `.sort`
method or `callback(*pargs, **kwargs)` as a catch all. The callback
should return a boolean True if the reverse should be canceled,
False or None otherwise."""
self._sort_callback = callback if callable(callback) else _void
def append(self, x):
"""Add an item to the end of the list."""
self[len(self):] = [x]
def extend(self, L):
"""Extend the list by appending all the items in the given list."""
self[len(self):] = L
def remove(self, x):
"""Remove the first item from the list whose value is x. It is an error
if there is no such item."""
del self[self.index(x)]
def pop(self, i=None):
"""Remove the item at the given position in the list, and return it. If
no index is specified, a.pop() removes and returns the last item in the
list."""
if i is None:
i = len(self) - 1
val = self[i]
del self[i]
return val
def reverse(self):
"""Reverse the elements of the list, in place."""
if self._can_reverse():
list.reverse(self)
def insert(self, index, value):
"""Insert an item at a given position. The first argument is the index
of the element before which to insert, so a.insert(0, x) inserts at the
front of the list, and a.insert(len(a), x) is equivalent to
a.append(x)."""
if self._can_insert(index, value):
list.insert(self, index, value)
def sort(self, *pargs, **kwargs):
"""Sort the items of the list in place (the arguments can be used for
sort customization, see Python's sorted() for their explanation)."""
if self._can_sort(*pargs, **kwargs):
list.sort(self, *pargs, **kwargs)
def __delitem__(self, index):
if self._can_del(index):
list.__delitem__(self, index)
def __setitem__(self, index, value):
if self._can_set(index, value):
list.__setitem__(self, index, value)
def __setslice__(self, start, end, value):
if self._can_set(slice(start, end), value):
list.__setslice__(self, start, end, value)
def _can_insert(self, index, value):
"""Check if the item can be inserted."""
return not bool(self._insert_callback(index, value))
def _can_del(self, index):
"""Check if the item can be deleted."""
return not bool(self._del_callback(index))
def _can_set(self, index, value):
"""Check if the item can be set."""
return not bool(self._set_callback(index, value))
def _can_reverse(self):
"""Check if the list can be reversed."""
return not bool(self._reverse_callback())
def _can_sort(self, *pargs, **kwargs):
"""Check if the list can be sorted."""
return not bool(self._sort_callback(*pargs, **kwargs))

@ -1,4 +1,4 @@
#!/bin/sh
#!/bin/bash
git submodule init
git submodule update

Loading…
Cancel
Save