##// END OF EJS Templates
Merge remote-tracking branch 'upstream/master'
Nathan Heijermans -
r19284:8d2b1c3b merge
parent child Browse files
Show More
@@ -0,0 +1,135 b''
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
3
4 define([
5 'jquery',
6 'base/js/namespace',
7 'base/js/utils',
8 'base/js/dialog',
9 ], function ($, IPython, utils, dialog) {
10 "use strict";
11
12 var NewNotebookWidget = function (selector, options) {
13 this.selector = selector;
14 this.base_url = options.base_url;
15 this.notebook_path = options.notebook_path;
16 this.contents = options.contents;
17 this.default_kernel = null;
18 this.config = options.config;
19 this.kernelspecs = {};
20 if (this.selector !== undefined) {
21 this.element = $(selector);
22 this.request_kernelspecs();
23 }
24 this.bind_events();
25 };
26
27 NewNotebookWidget.prototype.bind_events = function () {
28 var that = this;
29 this.element.find('#new_notebook').click(function () {
30 that.new_notebook();
31 });
32 };
33
34 NewNotebookWidget.prototype.request_kernelspecs = function () {
35 /** request and then load kernel specs */
36 var url = utils.url_join_encode(this.base_url, 'api/kernelspecs');
37 utils.promising_ajax(url).then($.proxy(this._load_kernelspecs, this));
38 };
39
40 NewNotebookWidget.prototype._load_kernelspecs = function (data) {
41 /** load kernelspec list */
42 var that = this;
43 this.kernelspecs = data.kernelspecs;
44 var menu = this.element.find("#new-notebook-menu");
45 var keys = Object.keys(data.kernelspecs).sort(function (a, b) {
46 var da = data.kernelspecs[a].display_name;
47 var db = data.kernelspecs[b].display_name;
48 if (da === db) {
49 return 0;
50 } else if (da > db) {
51 return 1;
52 } else {
53 return -1;
54 }
55 });
56 for (var i = 0; i < keys.length; i++) {
57 var ks = this.kernelspecs[keys[i]];
58 var li = $("<li>")
59 .attr("id", "kernel-" +ks.name)
60 .data('kernelspec', ks).append(
61 $('<a>')
62 .attr('href', '#')
63 .click($.proxy(this.new_notebook, this, ks.name))
64 .text(ks.display_name)
65 .attr('title', 'Create a new notebook with ' + ks.display_name)
66 );
67 menu.append(li);
68 }
69 this.config.loaded.then(function () {
70 that._load_default_kernelspec(data['default']);
71 });
72 };
73
74 NewNotebookWidget.prototype._load_default_kernelspec = function (default_name) {
75 /** load default kernelspec name from config, if defined */
76 if (this.config.data.NewNotebookWidget &&
77 this.config.data.NewNotebookWidget.default_kernel &&
78 this.kernelspecs[this.config.data.NewNotebookWidget.default_kernel] !== undefined
79 ) {
80 default_name = this.config.data.NewNotebookWidget.default_kernel;
81 }
82 this.set_default_kernel(default_name);
83 };
84
85 NewNotebookWidget.prototype.set_default_kernel = function (kernel_name) {
86 /** select the current default kernel */
87 this.default_kernel = kernel_name;
88 this.config.update({
89 NewNotebookWidget: {
90 default_kernel: kernel_name
91 }
92 });
93 var spec = this.kernelspecs[kernel_name];
94 var display_name;
95 if (spec) {
96 display_name = spec.display_name;
97 this.element.find("#current-kernel")
98 .text(display_name)
99 .attr('title', display_name + " is the default kernel for new notebooks");
100 } else {
101 display_name = 'default kernel';
102 }
103 this.element.find("#new_notebook").attr('title',
104 'Create a new notebook with ' + display_name
105 );
106 };
107
108 NewNotebookWidget.prototype.new_notebook = function (kernel_name) {
109 /** create and open a new notebook */
110 var that = this;
111 kernel_name = kernel_name || this.default_kernel;
112 var w = window.open();
113 this.contents.new_untitled(that.notebook_path, {type: "notebook"}).then(
114 function (data) {
115 var url = utils.url_join_encode(
116 that.base_url, 'notebooks', data.path
117 );
118 if (kernel_name) {
119 url += "?kernel_name=" + kernel_name;
120 }
121 w.location = url;
122 },
123 function (error) {
124 w.close();
125 dialog.modal({
126 title : 'Creating Notebook Failed',
127 body : "The error was: " + error.message,
128 buttons : {'OK' : {'class' : 'btn-primary'}}
129 });
130 }
131 );
132 };
133
134 return {'NewNotebookWidget': NewNotebookWidget};
135 });
@@ -277,7 +277,7 b' class Pdb(OldPdb):'
277 277 try:
278 278 OldPdb.interaction(self, frame, traceback)
279 279 except KeyboardInterrupt:
280 self.shell.write("\nKeyboardInterrupt\n")
280 self.shell.write('\n' + self.shell.get_exception_only())
281 281 break
282 282 else:
283 283 break
@@ -40,38 +40,21 b' else:'
40 40 #-----------------------------------------------------------------------------
41 41
42 42
43 def _valid_formatter(f):
44 """Return whether an object is a valid formatter
45
46 Cases checked:
43 def _safe_get_formatter_method(obj, name):
44 """Safely get a formatter method
47 45
48 - bound methods OK
49 - unbound methods NO
50 - callable with zero args OK
46 - Classes cannot have formatter methods, only instance
47 - protect against proxy objects that claim to have everything
51 48 """
52 if f is None:
53 return False
54 elif isinstance(f, type(str.find)):
55 # unbound methods on compiled classes have type method_descriptor
56 return False
57 elif isinstance(f, types.BuiltinFunctionType):
58 # bound methods on compiled classes have type builtin_function
59 return True
60 elif callable(f):
61 # anything that works with zero args should be okay
62 try:
63 inspect.getcallargs(f)
64 except Exception:
65 return False
66 else:
67 return True
68 return False
69
70 def _safe_get_formatter_method(obj, name):
71 """Safely get a formatter method"""
49 if inspect.isclass(obj):
50 # repr methods only make sense on instances, not classes
51 return None
72 52 method = pretty._safe_getattr(obj, name, None)
73 # formatter methods must be bound
74 if _valid_formatter(method):
53 if callable(method):
54 # obj claims to have repr method...
55 if callable(pretty._safe_getattr(obj, '_ipython_canary_method_should_not_exist_', None)):
56 # ...but don't trust proxy objects that claim to have everything
57 return None
75 58 return method
76 59
77 60
@@ -22,6 +22,7 b' import re'
22 22 import runpy
23 23 import sys
24 24 import tempfile
25 import traceback
25 26 import types
26 27 import subprocess
27 28 from io import open as io_open
@@ -1786,6 +1787,15 b' class InteractiveShell(SingletonConfigurable):'
1786 1787 """
1787 1788 self.write_err("UsageError: %s" % exc)
1788 1789
1790 def get_exception_only(self, exc_tuple=None):
1791 """
1792 Return as a string (ending with a newline) the exception that
1793 just occurred, without any traceback.
1794 """
1795 etype, value, tb = self._get_exc_info(exc_tuple)
1796 msg = traceback.format_exception_only(etype, value)
1797 return ''.join(msg)
1798
1789 1799 def showtraceback(self, exc_tuple=None, filename=None, tb_offset=None,
1790 1800 exception_only=False):
1791 1801 """Display the exception that just occurred.
@@ -1838,7 +1848,7 b' class InteractiveShell(SingletonConfigurable):'
1838 1848 self._showtraceback(etype, value, stb)
1839 1849
1840 1850 except KeyboardInterrupt:
1841 self.write_err("\nKeyboardInterrupt\n")
1851 self.write_err('\n' + self.get_exception_only())
1842 1852
1843 1853 def _showtraceback(self, etype, evalue, stb):
1844 1854 """Actually show a traceback.
@@ -1993,7 +2003,7 b' class InteractiveShell(SingletonConfigurable):'
1993 2003 continue
1994 2004
1995 2005 @skip_doctest
1996 def set_next_input(self, s):
2006 def set_next_input(self, s, replace=False):
1997 2007 """ Sets the 'default' input string for the next command line.
1998 2008
1999 2009 Requires readline.
@@ -2355,7 +2365,7 b' class InteractiveShell(SingletonConfigurable):'
2355 2365 try:
2356 2366 ec = os.system(cmd)
2357 2367 except KeyboardInterrupt:
2358 self.write_err("\nKeyboardInterrupt\n")
2368 self.write_err('\n' + self.get_exception_only())
2359 2369 ec = -2
2360 2370 else:
2361 2371 cmd = py3compat.unicode_to_str(cmd)
@@ -2374,7 +2384,7 b' class InteractiveShell(SingletonConfigurable):'
2374 2384 ec = subprocess.call(cmd, shell=True, executable=executable)
2375 2385 except KeyboardInterrupt:
2376 2386 # intercept control-C; a long traceback is not useful here
2377 self.write_err("\nKeyboardInterrupt\n")
2387 self.write_err('\n' + self.get_exception_only())
2378 2388 ec = 130
2379 2389 if ec > 128:
2380 2390 ec = -(ec - 128)
@@ -353,7 +353,9 b' class CodeMagics(Magics):'
353 353 print('Operation cancelled.')
354 354 return
355 355
356 self.shell.set_next_input(contents)
356 contents = "# %load {}\n".format(arg_s) + contents
357
358 self.shell.set_next_input(contents, replace=True)
357 359
358 360 @staticmethod
359 361 def _find_edit_target(shell, args, opts, last_call):
@@ -165,7 +165,6 b' class OSMagics(Magics):'
165 165
166 166 path = [os.path.abspath(os.path.expanduser(p)) for p in
167 167 os.environ.get('PATH','').split(os.pathsep)]
168 path = filter(os.path.isdir,path)
169 168
170 169 syscmdlist = []
171 170 # Now define isexec in a cross platform manner.
@@ -189,8 +188,12 b' class OSMagics(Magics):'
189 188 # the innermost part
190 189 if os.name == 'posix':
191 190 for pdir in path:
192 os.chdir(pdir)
193 for ff in os.listdir(pdir):
191 try:
192 os.chdir(pdir)
193 dirlist = os.listdir(pdir)
194 except OSError:
195 continue
196 for ff in dirlist:
194 197 if isexec(ff):
195 198 try:
196 199 # Removes dots from the name since ipython
@@ -205,8 +208,12 b' class OSMagics(Magics):'
205 208 else:
206 209 no_alias = Alias.blacklist
207 210 for pdir in path:
208 os.chdir(pdir)
209 for ff in os.listdir(pdir):
211 try:
212 os.chdir(pdir)
213 dirlist = os.listdir(pdir)
214 except OSError:
215 continue
216 for ff in dirlist:
210 217 base, ext = os.path.splitext(ff)
211 218 if isexec(ff) and base.lower() not in no_alias:
212 219 if ext.lower() == '.exe':
@@ -314,7 +314,6 b' def test_print_method_bound():'
314 314 class MyHTML(object):
315 315 def _repr_html_(self):
316 316 return "hello"
317
318 317 with capture_output() as captured:
319 318 result = f(MyHTML)
320 319 nt.assert_is(result, None)
@@ -325,6 +324,44 b' def test_print_method_bound():'
325 324 nt.assert_equal(result, "hello")
326 325 nt.assert_equal(captured.stderr, "")
327 326
327 def test_print_method_weird():
328
329 class TextMagicHat(object):
330 def __getattr__(self, key):
331 return key
332
333 f = HTMLFormatter()
334
335 text_hat = TextMagicHat()
336 nt.assert_equal(text_hat._repr_html_, '_repr_html_')
337 with capture_output() as captured:
338 result = f(text_hat)
339
340 nt.assert_is(result, None)
341 nt.assert_not_in("FormatterWarning", captured.stderr)
342
343 class CallableMagicHat(object):
344 def __getattr__(self, key):
345 return lambda : key
346
347 call_hat = CallableMagicHat()
348 with capture_output() as captured:
349 result = f(call_hat)
350
351 nt.assert_equal(result, None)
352
353 class BadReprArgs(object):
354 def _repr_html_(self, extra, args):
355 return "html"
356
357 bad = BadReprArgs()
358 with capture_output() as captured:
359 result = f(bad)
360
361 nt.assert_is(result, None)
362 nt.assert_not_in("FormatterWarning", captured.stderr)
363
364
328 365 def test_format_config():
329 366 """config objects don't pretend to support fancy reprs with lazy attrs"""
330 367 f = HTMLFormatter()
@@ -482,6 +482,24 b' class InteractiveShellTestCase(unittest.TestCase):'
482 482 mod = ip.new_main_mod(u'%s.py' % name, name)
483 483 self.assertEqual(mod.__name__, name)
484 484
485 def test_get_exception_only(self):
486 try:
487 raise KeyboardInterrupt
488 except KeyboardInterrupt:
489 msg = ip.get_exception_only()
490 self.assertEqual(msg, 'KeyboardInterrupt\n')
491
492 class DerivedInterrupt(KeyboardInterrupt):
493 pass
494 try:
495 raise DerivedInterrupt("foo")
496 except KeyboardInterrupt:
497 msg = ip.get_exception_only()
498 if sys.version_info[0] <= 2:
499 self.assertEqual(msg, 'DerivedInterrupt: foo\n')
500 else:
501 self.assertEqual(msg, 'IPython.core.tests.test_interactiveshell.DerivedInterrupt: foo\n')
502
485 503 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
486 504
487 505 @onlyif_unicode_paths
@@ -245,14 +245,16 b' def fix_frame_records_filenames(records):'
245 245 """
246 246 fixed_records = []
247 247 for frame, filename, line_no, func_name, lines, index in records:
248 # Look inside the frame's globals dictionary for __file__, which should
249 # be better.
250 better_fn = frame.f_globals.get('__file__', None)
251 if isinstance(better_fn, str):
252 # Check the type just in case someone did something weird with
253 # __file__. It might also be None if the error occurred during
254 # import.
255 filename = better_fn
248 # Look inside the frame's globals dictionary for __file__,
249 # which should be better. However, keep Cython filenames since
250 # we prefer the source filenames over the compiled .so file.
251 if not filename.endswith(('.pyx', '.pxd', '.pxi')):
252 better_fn = frame.f_globals.get('__file__', None)
253 if isinstance(better_fn, str):
254 # Check the type just in case someone did something weird with
255 # __file__. It might also be None if the error occurred during
256 # import.
257 filename = better_fn
256 258 fixed_records.append((frame, filename, line_no, func_name, lines, index))
257 259 return fixed_records
258 260
@@ -722,15 +724,23 b' class VerboseTB(TBTools):'
722 724 #print '*** record:',file,lnum,func,lines,index # dbg
723 725 if not file:
724 726 file = '?'
725 elif not (file.startswith(str("<")) and file.endswith(str(">"))):
726 # Guess that filenames like <string> aren't real filenames, so
727 # don't call abspath on them.
728 try:
729 file = abspath(file)
730 except OSError:
731 # Not sure if this can still happen: abspath now works with
732 # file names like <string>
733 pass
727 elif file.startswith(str("<")) and file.endswith(str(">")):
728 # Not a real filename, no problem...
729 pass
730 elif not os.path.isabs(file):
731 # Try to make the filename absolute by trying all
732 # sys.path entries (which is also what linecache does)
733 for dirname in sys.path:
734 try:
735 fullname = os.path.join(dirname, file)
736 if os.path.isfile(fullname):
737 file = os.path.abspath(fullname)
738 break
739 except Exception:
740 # Just in case that sys.path contains very
741 # strange entries...
742 pass
743
734 744 file = py3compat.cast_unicode(file, util_path.fs_encoding)
735 745 link = tpl_link % file
736 746 args, varargs, varkw, locals = inspect.getargvalues(frame)
@@ -611,9 +611,9 b' class FileContentsManager(ContentsManager):'
611 611 return "Serving notebooks from local directory: %s" % self.root_dir
612 612
613 613 def get_kernel_path(self, path, model=None):
614 """Return the initial working dir a kernel associated with a given notebook"""
614 """Return the initial API path of a kernel associated with a given notebook"""
615 615 if '/' in path:
616 616 parent_dir = path.rsplit('/', 1)[0]
617 617 else:
618 618 parent_dir = ''
619 return self._get_os_path(parent_dir)
619 return parent_dir
@@ -187,8 +187,12 b' class ContentsManager(LoggingConfigurable):'
187 187
188 188 KernelManagers can turn this value into a filesystem path,
189 189 or ignore it altogether.
190
191 The default value here will start kernels in the directory of the
192 notebook server. FileContentsManager overrides this to use the
193 directory containing the notebook.
190 194 """
191 return path
195 return ''
192 196
193 197 def increment_filename(self, filename, path='', insert=''):
194 198 """Increment a filename until it is unique.
@@ -54,14 +54,10 b' class MappingKernelManager(MultiKernelManager):'
54 54
55 55 def cwd_for_path(self, path):
56 56 """Turn API path into absolute OS path."""
57 # short circuit for NotebookManagers that pass in absolute paths
58 if os.path.exists(path):
59 return path
60
61 57 os_path = to_os_path(path, self.root_dir)
62 58 # in the case of notebooks and kernels not being on the same filesystem,
63 59 # walk up to root_dir if the paths don't exist
64 while not os.path.exists(os_path) and os_path != self.root_dir:
60 while not os.path.isdir(os_path) and os_path != self.root_dir:
65 61 os_path = os.path.dirname(os_path)
66 62 return os_path
67 63
@@ -7,8 +7,6 b' from tornado import web'
7 7
8 8 from ...base.handlers import IPythonHandler, json_errors
9 9
10 from IPython.kernel.kernelspec import _pythonfirst
11
12 10
13 11 class MainKernelSpecHandler(IPythonHandler):
14 12 SUPPORTED_METHODS = ('GET',)
@@ -17,18 +15,21 b' class MainKernelSpecHandler(IPythonHandler):'
17 15 @json_errors
18 16 def get(self):
19 17 ksm = self.kernel_spec_manager
20 results = []
21 for kernel_name in sorted(ksm.find_kernel_specs(), key=_pythonfirst):
18 km = self.kernel_manager
19 model = {}
20 model['default'] = km.default_kernel_name
21 model['kernelspecs'] = specs = {}
22 for kernel_name in ksm.find_kernel_specs():
22 23 try:
23 24 d = ksm.get_kernel_spec(kernel_name).to_dict()
24 25 except Exception:
25 26 self.log.error("Failed to load kernel spec: '%s'", kernel_name, exc_info=True)
26 27 continue
27 28 d['name'] = kernel_name
28 results.append(d)
29 specs[kernel_name] = d
29 30
30 31 self.set_header("Content-Type", 'application/json')
31 self.finish(json.dumps(results))
32 self.finish(json.dumps(model))
32 33
33 34
34 35 class KernelSpecHandler(IPythonHandler):
@@ -78,16 +78,22 b' class APITest(NotebookTestBase):'
78 78 with open(pjoin(bad_kernel_dir, 'kernel.json'), 'w') as f:
79 79 f.write("garbage")
80 80
81 specs = self.ks_api.list().json()
82 assert isinstance(specs, list)
81 model = self.ks_api.list().json()
82 assert isinstance(model, dict)
83 self.assertEqual(model['default'], NATIVE_KERNEL_NAME)
84 specs = model['kernelspecs']
85 assert isinstance(specs, dict)
83 86 # 2: the sample kernelspec created in setUp, and the native Python kernel
84 87 self.assertGreaterEqual(len(specs), 2)
85 88
86 89 shutil.rmtree(bad_kernel_dir)
87 90
88 91 def test_list_kernelspecs(self):
89 specs = self.ks_api.list().json()
90 assert isinstance(specs, list)
92 model = self.ks_api.list().json()
93 assert isinstance(model, dict)
94 self.assertEqual(model['default'], NATIVE_KERNEL_NAME)
95 specs = model['kernelspecs']
96 assert isinstance(specs, dict)
91 97
92 98 # 2: the sample kernelspec created in setUp, and the native Python kernel
93 99 self.assertGreaterEqual(len(specs), 2)
@@ -98,8 +104,8 b' class APITest(NotebookTestBase):'
98 104 def is_default_kernelspec(s):
99 105 return s['name'] == NATIVE_KERNEL_NAME and s['display_name'].startswith("IPython")
100 106
101 assert any(is_sample_kernelspec(s) for s in specs), specs
102 assert any(is_default_kernelspec(s) for s in specs), specs
107 assert any(is_sample_kernelspec(s) for s in specs.values()), specs
108 assert any(is_default_kernelspec(s) for s in specs.values()), specs
103 109
104 110 def test_get_kernelspec(self):
105 111 spec = self.ks_api.kernel_spec_info('Sample').json() # Case insensitive
@@ -4,14 +4,17 b''
4 4 define([
5 5 'base/js/namespace',
6 6 'jquery',
7 ], function(IPython, $){
7 'base/js/events',
8 ], function(IPython, $, events){
8 9 "use strict";
9 10
10 11 var Page = function () {
11 12 this.bind_events();
13 this._resize_header();
12 14 };
13 15
14 16 Page.prototype.bind_events = function () {
17 events.on('resize-header.Page', $.proxy(this._resize_header, this));
15 18 };
16 19
17 20 Page.prototype.show = function () {
@@ -41,6 +44,11 b' define(['
41 44 $('div#site').css('display','block');
42 45 };
43 46
47 Page.prototype._resize_header = function() {
48 // Update the header's size.
49 $('#header-spacer').height($('#header').height());
50 };
51
44 52 // Register self in the global namespace for convenience.
45 53 IPython.Page = Page;
46 54 return {'Page': Page};
@@ -541,7 +541,16 b' define(['
541 541 if (navigator.appVersion.indexOf("Linux")!=-1) OSName="Linux";
542 542 return OSName;
543 543 })();
544
544
545 var get_url_param = function (name) {
546 // get a URL parameter. I cannot believe we actually need this.
547 // Based on http://stackoverflow.com/a/25359264/938949
548 var match = new RegExp('[\?&]' + name + '=([^&]*)').exec(window.location.search);
549 if (match){
550 return decodeURIComponent(match[1] || '');
551 }
552 };
553
545 554 var is_or_has = function (a, b) {
546 555 /**
547 556 * Is b a child of a or a itself?
@@ -632,6 +641,7 b' define(['
632 641 * Like $.ajax, but returning an ES6 promise. success and error settings
633 642 * will be ignored.
634 643 */
644 settings = settings || {};
635 645 return new Promise(function(resolve, reject) {
636 646 settings.success = function(data, status, jqXHR) {
637 647 resolve(data);
@@ -766,6 +776,33 b' define(['
766 776 };
767 777 };
768 778
779 var typeset = function(element, text) {
780 /**
781 * Apply MathJax rendering to an element, and optionally set its text
782 *
783 * If MathJax is not available, make no changes.
784 *
785 * Returns the output any number of typeset elements, or undefined if
786 * MathJax was not available.
787 *
788 * Parameters
789 * ----------
790 * element: Node, NodeList, or jQuery selection
791 * text: option string
792 */
793 if(!window.MathJax){
794 return;
795 }
796 var $el = element.jquery ? element : $(element);
797 if(arguments.length > 1){
798 $el.text(text);
799 }
800 return $el.map(function(){
801 // MathJax takes a DOM node: $.map makes `this` the context
802 return MathJax.Hub.Queue(["Typeset", MathJax.Hub, this]);
803 });
804 };
805
769 806 var utils = {
770 807 regex_split : regex_split,
771 808 uuid : uuid,
@@ -786,6 +823,7 b' define(['
786 823 from_absolute_cursor_pos : from_absolute_cursor_pos,
787 824 browser : browser,
788 825 platform: platform,
826 get_url_param: get_url_param,
789 827 is_or_has : is_or_has,
790 828 is_focused : is_focused,
791 829 mergeopt: mergeopt,
@@ -799,6 +837,7 b' define(['
799 837 load_class: load_class,
800 838 resolve_promises_dict: resolve_promises_dict,
801 839 reject: reject,
840 typeset: typeset,
802 841 };
803 842
804 843 // Backwards compatability.
@@ -20,11 +20,33 b' body {'
20 20 div#header {
21 21 /* Initially hidden to prevent FLOUC */
22 22 display: none;
23 margin-bottom: 0px;
24 padding-left: 30px;
25 padding-bottom: 5px;
26 border-bottom: 1px solid @navbar-default-border;
27 .border-box-sizing();
23 margin-bottom: -6px;
24 position: fixed;
25 top: 0;
26 width: 100%;
27 background-color: @body-bg;
28 min-height: 31px;
29
30 /* Display over codemirror */
31 z-index: 100;
32
33 #header-container {
34 margin-bottom: 0px;
35 padding-left: 30px;
36 padding-bottom: 5px;
37 .border-box-sizing();
38 }
39
40 .header-bar {
41 width: 100%;
42 height: 0px;
43 border-bottom: 1px solid @navbar-default-border;
44 }
45 }
46
47 #header-spacer {
48 width: 100%;
49 visibility: hidden;
28 50 }
29 51
30 52 #ipython_notebook {
@@ -220,10 +220,7 b' define(['
220 220 * @method typeset
221 221 */
222 222 Cell.prototype.typeset = function () {
223 if (window.MathJax) {
224 var cell_math = this.element.get(0);
225 MathJax.Hub.Queue(["Typeset", MathJax.Hub, cell_math]);
226 }
223 utils.typeset(this.element);
227 224 };
228 225
229 226 /**
@@ -389,7 +389,7 b' define(['
389 389 * @private
390 390 */
391 391 CodeCell.prototype._handle_set_next_input = function (payload) {
392 var data = {'cell': this, 'text': payload.text};
392 var data = {'cell': this, 'text': payload.text, replace: payload.replace};
393 393 this.events.trigger('set_next_input.Notebook', data);
394 394 };
395 395
@@ -25,16 +25,27 b' define(['
25 25
26 26 KernelSelector.prototype.request_kernelspecs = function() {
27 27 var url = utils.url_join_encode(this.notebook.base_url, 'api/kernelspecs');
28 $.ajax(url, {success: $.proxy(this._got_kernelspecs, this)});
28 utils.promising_ajax(url).then($.proxy(this._got_kernelspecs, this));
29 29 };
30 30
31 KernelSelector.prototype._got_kernelspecs = function(data, status, xhr) {
32 this.kernelspecs = {};
31 KernelSelector.prototype._got_kernelspecs = function(data) {
32 this.kernelspecs = data.kernelspecs;
33 33 var menu = this.element.find("#kernel_selector");
34 34 var change_kernel_submenu = $("#menu-change-kernel-submenu");
35 for (var i = 0; i < data.length; i++) {
36 var ks = data[i];
37 this.kernelspecs[ks.name] = ks;
35 var keys = Object.keys(data.kernelspecs).sort(function (a, b) {
36 // sort by display_name
37 var da = data.kernelspecs[a].display_name;
38 var db = data.kernelspecs[b].display_name;
39 if (da === db) {
40 return 0;
41 } else if (da > db) {
42 return 1;
43 } else {
44 return -1;
45 }
46 });
47 for (var i = 0; i < keys.length; i++) {
48 var ks = this.kernelspecs[keys[i]];
38 49 var ksentry = $("<li>").attr("id", "kernel-" +ks.name).append($('<a>')
39 50 .attr('href', '#')
40 51 .click($.proxy(this.change_kernel, this, ks.name))
@@ -9,7 +9,6 b' require(['
9 9 'services/config',
10 10 'base/js/utils',
11 11 'base/js/page',
12 'notebook/js/layoutmanager',
13 12 'base/js/events',
14 13 'auth/js/loginwidget',
15 14 'notebook/js/maintoolbar',
@@ -34,7 +33,6 b' require(['
34 33 configmod,
35 34 utils,
36 35 page,
37 layoutmanager,
38 36 events,
39 37 loginwidget,
40 38 maintoolbar,
@@ -66,9 +64,7 b' require(['
66 64
67 65 var user_config = $.extend({}, config.default_config);
68 66 var page = new page.Page();
69 var layout_manager = new layoutmanager.LayoutManager();
70 var pager = new pager.Pager('div#pager', 'div#pager_splitter', {
71 layout_manager: layout_manager,
67 var pager = new pager.Pager('div#pager', {
72 68 events: events});
73 69 var acts = new actions.init();
74 70 var keyboard_manager = new keyboardmanager.KeyboardManager({
@@ -104,7 +100,6 b' require(['
104 100 var menubar = new menubar.MenuBar('#menubar', $.extend({
105 101 notebook: notebook,
106 102 contents: contents,
107 layout_manager: layout_manager,
108 103 events: events,
109 104 save_widget: save_widget,
110 105 quick_help: quick_help},
@@ -132,9 +127,7 b' require(['
132 127
133 128 page.show();
134 129
135 layout_manager.do_resize();
136 130 var first_load = function () {
137 layout_manager.do_resize();
138 131 var hash = document.location.hash;
139 132 if (hash) {
140 133 document.location.hash = '';
@@ -147,7 +140,6 b' require(['
147 140 events.on('notebook_loaded.Notebook', first_load);
148 141
149 142 IPython.page = page;
150 IPython.layout_manager = layout_manager;
151 143 IPython.notebook = notebook;
152 144 IPython.contents = contents;
153 145 IPython.pager = pager;
@@ -24,7 +24,6 b' define(['
24 24 * Dictionary of keyword arguments.
25 25 * notebook: Notebook instance
26 26 * contents: ContentManager instance
27 * layout_manager: LayoutManager instance
28 27 * events: $(Events) instance
29 28 * save_widget: SaveWidget instance
30 29 * quick_help: QuickHelp instance
@@ -37,7 +36,6 b' define(['
37 36 this.selector = selector;
38 37 this.notebook = options.notebook;
39 38 this.contents = options.contents;
40 this.layout_manager = options.layout_manager;
41 39 this.events = options.events;
42 40 this.save_widget = options.save_widget;
43 41 this.quick_help = options.quick_help;
@@ -88,6 +86,13 b' define(['
88 86 }
89 87 };
90 88
89 MenuBar.prototype._size_header = function() {
90 /**
91 * Update header spacer size.
92 */
93 this.events.trigger('resize-header.Page');
94 };
95
91 96 MenuBar.prototype.bind_events = function () {
92 97 /**
93 98 * File
@@ -149,6 +154,10 b' define(['
149 154 that._nbconvert('pdf', true);
150 155 });
151 156
157 this.element.find('#download_script').click(function () {
158 that._nbconvert('script', true);
159 });
160
152 161 this.element.find('#rename_notebook').click(function () {
153 162 that.save_widget.rename_notebook({notebook: that.notebook});
154 163 });
@@ -218,12 +227,12 b' define(['
218 227
219 228 // View
220 229 this.element.find('#toggle_header').click(function () {
221 $('div#header').toggle();
222 that.layout_manager.do_resize();
230 $('div#header-container').toggle();
231 that._size_header();
223 232 });
224 233 this.element.find('#toggle_toolbar').click(function () {
225 234 $('div#maintoolbar').toggle();
226 that.layout_manager.do_resize();
235 that._size_header();
227 236 });
228 237 // Insert
229 238 this.element.find('#insert_cell_above').click(function () {
@@ -371,20 +380,6 b' define(['
371 380 var langname = (langinfo.name || 'Script')
372 381 langname = langname.charAt(0).toUpperCase()+langname.substr(1) // Capitalise
373 382 el.find('a').text(langname + ' ('+(langinfo.file_extension || 'txt')+')');
374
375 // Unregister any previously registered handlers
376 el.off('click');
377 if (langinfo.nbconvert_exporter) {
378 // Metadata specifies a specific exporter, e.g. 'python'
379 el.click(function() {
380 that._nbconvert(langinfo.nbconvert_exporter, true);
381 });
382 } else {
383 // Use generic 'script' exporter
384 el.click(function() {
385 that._nbconvert('script', true);
386 });
387 }
388 383 };
389 384
390 385 // Backwards compatability.
@@ -210,9 +210,14 b' define(['
210 210 var that = this;
211 211
212 212 this.events.on('set_next_input.Notebook', function (event, data) {
213 var index = that.find_cell_index(data.cell);
214 var new_cell = that.insert_cell_below('code',index);
215 new_cell.set_text(data.text);
213 if (data.replace) {
214 data.cell.set_text(data.text);
215 data.cell.clear_output();
216 } else {
217 var index = that.find_cell_index(data.cell);
218 var new_cell = that.insert_cell_below('code',index);
219 new_cell.set_text(data.text);
220 }
216 221 that.dirty = true;
217 222 });
218 223
@@ -2326,9 +2331,13 b' define(['
2326 2331 // Create the session after the notebook is completely loaded to prevent
2327 2332 // code execution upon loading, which is a security risk.
2328 2333 if (this.session === null) {
2329 var kernelspec = this.metadata.kernelspec || {};
2330 var kernel_name = kernelspec.name;
2331
2334 var kernel_name;
2335 if (this.metadata.kernelspec) {
2336 var kernelspec = this.metadata.kernelspec || {};
2337 kernel_name = kernelspec.name;
2338 } else {
2339 kernel_name = utils.get_url_param('kernel_name');
2340 }
2332 2341 this.start_session(kernel_name);
2333 2342 }
2334 2343 // load our checkpoint list
@@ -199,9 +199,7 b' define(['
199 199
200 200 // typeset with MathJax if MathJax is available
201 201 OutputArea.prototype.typeset = function () {
202 if (window.MathJax){
203 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
204 }
202 utils.typeset(this.element);
205 203 };
206 204
207 205
@@ -8,48 +8,22 b' define(['
8 8 ], function(IPython, $, utils) {
9 9 "use strict";
10 10
11 var Pager = function (pager_selector, pager_splitter_selector, options) {
11 var Pager = function (pager_selector, options) {
12 12 /**
13 13 * Constructor
14 14 *
15 15 * Parameters:
16 16 * pager_selector: string
17 * pager_splitter_selector: string
18 17 * options: dictionary
19 18 * Dictionary of keyword arguments.
20 19 * events: $(Events) instance
21 * layout_manager: LayoutManager instance
22 20 */
23 21 this.events = options.events;
24 22 this.pager_element = $(pager_selector);
25 this.pager_button_area = $('#pager_button_area');
26 var that = this;
27 this.percentage_height = 0.40;
28 options.layout_manager.pager = this;
29 this.pager_splitter_element = $(pager_splitter_selector)
30 .draggable({
31 containment: 'window',
32 axis:'y',
33 helper: null ,
34 drag: function(event, ui) {
35 /**
36 * recalculate the amount of space the pager should take
37 */
38 var pheight = ($(document.body).height()-event.clientY-4);
39 var downprct = pheight/options.layout_manager.app_height();
40 downprct = Math.min(0.9, downprct);
41 if (downprct < 0.1) {
42 that.percentage_height = 0.1;
43 that.collapse({'duration':0});
44 } else if (downprct > 0.2) {
45 that.percentage_height = downprct;
46 that.expand({'duration':0});
47 }
48 options.layout_manager.do_resize();
49 }
50 });
23 this.pager_button_area = $('#pager-button-area');
24 this._default_end_space = 200;
25 this.pager_element.resizable({handles: 'n', resize: $.proxy(this._resize, this)});
51 26 this.expanded = false;
52 this.style();
53 27 this.create_button_area();
54 28 this.bind_events();
55 29 };
@@ -61,7 +35,6 b' define(['
61 35 .attr('title',"Open the pager in an external window")
62 36 .addClass('ui-button')
63 37 .click(function(){that.detach();})
64 .attr('style','position: absolute; right: 20px;')
65 38 .append(
66 39 $('<span>').addClass("ui-icon ui-icon-extlink")
67 40 )
@@ -71,49 +44,37 b' define(['
71 44 .attr('title',"Close the pager")
72 45 .addClass('ui-button')
73 46 .click(function(){that.collapse();})
74 .attr('style','position: absolute; right: 5px;')
75 47 .append(
76 48 $('<span>').addClass("ui-icon ui-icon-close")
77 49 )
78 50 );
79 51 };
80 52
81 Pager.prototype.style = function () {
82 this.pager_splitter_element.addClass('ui-widget ui-state-default');
83 this.pager_splitter_element.attr('title', 'Click to Show/Hide pager area, drag to Resize');
84 };
85
86 53
87 54 Pager.prototype.bind_events = function () {
88 55 var that = this;
89 56
90 57 this.pager_element.bind('collapse_pager', function (event, extrap) {
91 var time = 'fast';
92 if (extrap && extrap.duration) {
93 time = extrap.duration;
94 }
95 that.pager_element.hide(time);
58 // Animate hiding of the pager.
59 var time = (extrap && extrap.duration) ? extrap.duration : 'fast';
60 that.pager_element.hide(time, function() {
61 $('.end_space').css('height', that._default_end_space);
62 });
96 63 });
97 64
98 65 this.pager_element.bind('expand_pager', function (event, extrap) {
99 var time = 'fast';
100 if (extrap && extrap.duration) {
101 time = extrap.duration;
102 }
103 that.pager_element.show(time);
104 });
105
106 this.pager_splitter_element.hover(
107 function () {
108 that.pager_splitter_element.addClass('ui-state-hover');
109 },
110 function () {
111 that.pager_splitter_element.removeClass('ui-state-hover');
112 }
113 );
114
115 this.pager_splitter_element.click(function () {
116 that.toggle();
66 // Clear the pager's height attr if it's set. This allows the
67 // pager to size itself according to its contents.
68 that.pager_element.height('initial');
69
70 // Animate the showing of the pager
71 var time = (extrap && extrap.duration) ? extrap.duration : 'fast';
72 that.pager_element.show(time, function() {
73 // Explicitly set pager height once the pager has shown itself.
74 // This allows the pager-contents div to use percentage sizing.
75 that.pager_element.height(that.pager_element.height());
76 that._resize();
77 });
117 78 });
118 79
119 80 this.events.on('open_with_text.Pager', function (event, payload) {
@@ -130,7 +91,7 b' define(['
130 91 Pager.prototype.collapse = function (extrap) {
131 92 if (this.expanded === true) {
132 93 this.expanded = false;
133 this.pager_element.add($('div#notebook')).trigger('collapse_pager', extrap);
94 this.pager_element.trigger('collapse_pager', extrap);
134 95 }
135 96 };
136 97
@@ -138,7 +99,7 b' define(['
138 99 Pager.prototype.expand = function (extrap) {
139 100 if (this.expanded !== true) {
140 101 this.expanded = true;
141 this.pager_element.add($('div#notebook')).trigger('expand_pager', extrap);
102 this.pager_element.trigger('expand_pager', extrap);
142 103 }
143 104 };
144 105
@@ -184,6 +145,18 b' define(['
184 145 this.pager_element.find(".container").append($('<pre/>').html(utils.fixCarriageReturn(utils.fixConsole(text))));
185 146 };
186 147
148
149 Pager.prototype._resize = function() {
150 /**
151 * Update document based on pager size.
152 */
153
154 // Make sure the padding at the end of the notebook is large
155 // enough that the user can scroll to the bottom of the
156 // notebook.
157 $('.end_space').css('height', Math.max(this.pager_element.height(), this._default_end_space));
158 };
159
187 160 // Backwards compatability.
188 161 IPython.Pager = Pager;
189 162
@@ -1,12 +1,13 b''
1 1 #menubar {
2 2 margin-top: 0px;
3 margin-bottom: -19px;
3 margin-bottom: -24px;
4 4 position: relative;
5 5 .border-box-sizing();
6 6
7 7 .navbar {
8 8 border-top: 1px;
9 9 border-radius: 0px 0px @border-radius-base @border-radius-base;
10 margin-bottom: 23px;
10 11 }
11 12
12 13 .navbar-toggle {
@@ -3,10 +3,6 b' body {'
3 3 background-color: @body-bg;
4 4 }
5 5
6 body.notebook_app {
7 overflow: hidden;
8 }
9
10 6 @media (max-width: 767px) {
11 7 // remove bootstrap-responsive's body padding on small screens
12 8 body.notebook_app {
@@ -36,19 +32,17 b' span#notebook_name {'
36 32 div#notebook_panel {
37 33 margin: 0px 0px 0px 0px;
38 34 padding: 0px;
39 .box-shadow(@notebook-shadow);
40 35 .border-box-sizing();
41 36 }
42 37 div#notebook {
43 38 font-size: @notebook_font_size;
44 39 line-height: @notebook_line_height;
45 overflow-y: scroll;
40 overflow-y: hidden;
46 41 overflow-x: auto;
47 42 width: 100%;
48 43 /* This spaces the cell away from the edge of the notebook area */
49 padding: 1em 0 1em 0;
44 padding: 2em 0 2em 0;
50 45 margin: 0px;
51 border-top: 1px solid @navbar-default-border;
52 46 outline: none;
53 47 .border-box-sizing();
54 48 }
@@ -86,3 +80,14 b' p {'
86 80 .end_space {
87 81 height: 200px;
88 82 }
83
84 .lower-header-bar {
85 width: 100%;
86 height: 0px;
87 border-bottom: 1px solid @navbar-default-border;
88 margin-bottom: -1px;
89 }
90
91 .notebook_app #header {
92 .box-shadow(@notebook-shadow);
93 }
@@ -1,25 +1,51 b''
1 div#pager_splitter {
2 height: 8px;
3 .border-box-sizing();
4 }
5
6 #pager-container {
7 position: relative;
8 padding: 15px 0px;
9 .border-box-sizing();
10 }
11
12 1 div#pager {
2 background-color: @body-bg;
13 3 font-size: @notebook_font_size;
14 4 line-height: @notebook_line_height;
15 overflow: auto;
5 overflow: hidden;
16 6 display: none;
7 position: fixed;
8 bottom: 0px;
9 width: 100%;
10 max-height: 50%;
11 padding-top: 7px;
12
13 /* Display over codemirror */
14 z-index: 100;
15
16 /* Hack which prevents jquery ui resizable from changing top. */
17 top: inherit !important;
17 18
18 19 pre {
19 line-height: @code_line_height;
20 color: @text-color;
21 background-color: @cell_background;
22 padding: @code_padding;
20 line-height: @code_line_height;
21 color: @text-color;
22 background-color: @cell_background;
23 padding: @code_padding;
24 }
25
26 #pager-button-area {
27 position: absolute;
28 top: 7px;
29 right: 20px;
30 }
31
32 #pager-contents {
33 position: relative;
34 overflow: auto;
35 width: 100%;
36 height: 100%;
37
38 #pager-container {
39 position: relative;
40 padding: 15px 0px;
41 .border-box-sizing();
42 }
43 }
44
45 .ui-resizable-handle {
46 top: 0px;
47 height: 7px;
48 background: @light_border_color;
49 border-bottom: 1px solid @border_color;
23 50 }
24 .border-box-sizing();
25 51 }
@@ -2,6 +2,7 b''
2 2 padding: 0px;
3 3 margin-left: -5px;
4 4 margin-top: -5px;
5 margin-bottom: 5px;
5 6
6 7 select, label {
7 8 width: auto;
@@ -33,8 +34,8 b''
33 34 border: 0px;
34 35 min-height: 27px;
35 36 margin-left: 32px;
36 padding-top: 6px;
37 padding-bottom: 8px;
37 padding-top: 11px;
38 padding-bottom: 3px;
38 39
39 40 .navbar-text {
40 41 float: none;
@@ -44,10 +45,6 b''
44 45 margin-right: 0px;
45 46 margin-top: 0px;
46 47 }
47
48 .toolbar {
49 margin-top: 0px;
50 }
51 48 }
52 49
53 50 .select-xs {
@@ -10,7 +10,7 b''
10 10 @notebook_line_height: 20px;
11 11 @code_line_height: 1.21429em; // changed from 1.231 to get 17px even
12 12 @code_padding: 0.4em; // 5.6 px
13 @notebook-shadow: inset 1px 4px 9px -6px rgba(0,0,0,.25);
13 @notebook-shadow: 1px 4px 9px -6px rgba(0,0,0,.25);
14 14 @rendered_html_border_color: black;
15 15 @input_prompt_color: navy;
16 16 @output_prompt_color: darkred;
@@ -7751,14 +7751,32 b' body {'
7751 7751 div#header {
7752 7752 /* Initially hidden to prevent FLOUC */
7753 7753 display: none;
7754 margin-bottom: -6px;
7755 position: fixed;
7756 top: 0;
7757 width: 100%;
7758 background-color: #ffffff;
7759 min-height: 31px;
7760 /* Display over codemirror */
7761 z-index: 100;
7762 }
7763 div#header #header-container {
7754 7764 margin-bottom: 0px;
7755 7765 padding-left: 30px;
7756 7766 padding-bottom: 5px;
7757 border-bottom: 1px solid #e7e7e7;
7758 7767 box-sizing: border-box;
7759 7768 -moz-box-sizing: border-box;
7760 7769 -webkit-box-sizing: border-box;
7761 7770 }
7771 div#header .header-bar {
7772 width: 100%;
7773 height: 0px;
7774 border-bottom: 1px solid #e7e7e7;
7775 }
7776 #header-spacer {
7777 width: 100%;
7778 visibility: hidden;
7779 }
7762 7780 #ipython_notebook {
7763 7781 padding-left: 0px;
7764 7782 }
@@ -7981,14 +7999,15 b' span#login_widget > .button .badge,'
7981 7999 margin: 0;
7982 8000 }
7983 8001 .alternate_upload input.fileinput {
7984 background-color: red;
7985 position: relative;
8002 display: inline;
7986 8003 opacity: 0;
7987 8004 z-index: 2;
7988 width: 295px;
7989 margin-left: 163px;
7990 cursor: pointer;
7991 height: 26px;
8005 width: 12ex;
8006 margin-right: -12ex;
8007 }
8008 .alternate_upload .input-overlay {
8009 display: inline-block;
8010 font-weight: bold;
7992 8011 }
7993 8012 /**
7994 8013 * Primary styles
@@ -8146,6 +8165,21 b' input.engine_num_input {'
8146 8165 .file_icon:before.pull-right {
8147 8166 margin-left: .3em;
8148 8167 }
8168 ul#new-notebook-menu {
8169 left: auto;
8170 right: 0;
8171 }
8172 .kernel-menu-icon {
8173 padding-right: 12px;
8174 width: 24px;
8175 content: "\f096";
8176 }
8177 .kernel-menu-icon:before {
8178 content: "\f096";
8179 }
8180 .kernel-menu-icon-current:before {
8181 content: "\f00c";
8182 }
8149 8183 /*!
8150 8184 *
8151 8185 * IPython notebook
@@ -9461,9 +9495,6 b' h6:hover .anchor-link {'
9461 9495 body {
9462 9496 background-color: #ffffff;
9463 9497 }
9464 body.notebook_app {
9465 overflow: hidden;
9466 }
9467 9498 @media (max-width: 767px) {
9468 9499 body.notebook_app {
9469 9500 padding-left: 0px;
@@ -9489,8 +9520,6 b' span#notebook_name:hover {'
9489 9520 div#notebook_panel {
9490 9521 margin: 0px 0px 0px 0px;
9491 9522 padding: 0px;
9492 -webkit-box-shadow: inset 1px 4px 9px -6px rgba(0, 0, 0, 0.25);
9493 box-shadow: inset 1px 4px 9px -6px rgba(0, 0, 0, 0.25);
9494 9523 box-sizing: border-box;
9495 9524 -moz-box-sizing: border-box;
9496 9525 -webkit-box-sizing: border-box;
@@ -9498,13 +9527,12 b' div#notebook_panel {'
9498 9527 div#notebook {
9499 9528 font-size: 14px;
9500 9529 line-height: 20px;
9501 overflow-y: scroll;
9530 overflow-y: hidden;
9502 9531 overflow-x: auto;
9503 9532 width: 100%;
9504 9533 /* This spaces the cell away from the edge of the notebook area */
9505 padding: 1em 0 1em 0;
9534 padding: 2em 0 2em 0;
9506 9535 margin: 0px;
9507 border-top: 1px solid #e7e7e7;
9508 9536 outline: none;
9509 9537 box-sizing: border-box;
9510 9538 -moz-box-sizing: border-box;
@@ -9542,6 +9570,16 b' p {'
9542 9570 .end_space {
9543 9571 height: 200px;
9544 9572 }
9573 .lower-header-bar {
9574 width: 100%;
9575 height: 0px;
9576 border-bottom: 1px solid #e7e7e7;
9577 margin-bottom: -1px;
9578 }
9579 .notebook_app #header {
9580 -webkit-box-shadow: 1px 4px 9px -6px rgba(0, 0, 0, 0.25);
9581 box-shadow: 1px 4px 9px -6px rgba(0, 0, 0, 0.25);
9582 }
9545 9583 /* CSS for the cell toolbar */
9546 9584 .celltoolbar {
9547 9585 border: thin solid #CFCFCF;
@@ -9779,7 +9817,7 b' fieldset[disabled] #kernel_selector_widget > button.active {'
9779 9817 }
9780 9818 #menubar {
9781 9819 margin-top: 0px;
9782 margin-bottom: -19px;
9820 margin-bottom: -24px;
9783 9821 position: relative;
9784 9822 box-sizing: border-box;
9785 9823 -moz-box-sizing: border-box;
@@ -9788,6 +9826,7 b' fieldset[disabled] #kernel_selector_widget > button.active {'
9788 9826 #menubar .navbar {
9789 9827 border-top: 1px;
9790 9828 border-radius: 0px 0px 4px 4px;
9829 margin-bottom: 23px;
9791 9830 }
9792 9831 #menubar .navbar-toggle {
9793 9832 float: left;
@@ -10256,27 +10295,21 b' fieldset[disabled] .notification_widget.danger.active {'
10256 10295 color: #d9534f;
10257 10296 background-color: #ffffff;
10258 10297 }
10259 div#pager_splitter {
10260 height: 8px;
10261 box-sizing: border-box;
10262 -moz-box-sizing: border-box;
10263 -webkit-box-sizing: border-box;
10264 }
10265 #pager-container {
10266 position: relative;
10267 padding: 15px 0px;
10268 box-sizing: border-box;
10269 -moz-box-sizing: border-box;
10270 -webkit-box-sizing: border-box;
10271 }
10272 10298 div#pager {
10299 background-color: #ffffff;
10273 10300 font-size: 14px;
10274 10301 line-height: 20px;
10275 overflow: auto;
10302 overflow: hidden;
10276 10303 display: none;
10277 box-sizing: border-box;
10278 -moz-box-sizing: border-box;
10279 -webkit-box-sizing: border-box;
10304 position: fixed;
10305 bottom: 0px;
10306 width: 100%;
10307 max-height: 50%;
10308 padding-top: 7px;
10309 /* Display over codemirror */
10310 z-index: 100;
10311 /* Hack which prevents jquery ui resizable from changing top. */
10312 top: inherit !important;
10280 10313 }
10281 10314 div#pager pre {
10282 10315 line-height: 1.21429em;
@@ -10284,6 +10317,30 b' div#pager pre {'
10284 10317 background-color: #f7f7f7;
10285 10318 padding: 0.4em;
10286 10319 }
10320 div#pager #pager-button-area {
10321 position: absolute;
10322 top: 7px;
10323 right: 20px;
10324 }
10325 div#pager #pager-contents {
10326 position: relative;
10327 overflow: auto;
10328 width: 100%;
10329 height: 100%;
10330 }
10331 div#pager #pager-contents #pager-container {
10332 position: relative;
10333 padding: 15px 0px;
10334 box-sizing: border-box;
10335 -moz-box-sizing: border-box;
10336 -webkit-box-sizing: border-box;
10337 }
10338 div#pager .ui-resizable-handle {
10339 top: 0px;
10340 height: 7px;
10341 background: #cfcfcf;
10342 border-bottom: 1px solid #ababab;
10343 }
10287 10344 .quickhelp {
10288 10345 /* Old browsers */
10289 10346 display: -webkit-box;
@@ -10350,6 +10407,7 b' span#autosave_status {'
10350 10407 padding: 0px;
10351 10408 margin-left: -5px;
10352 10409 margin-top: -5px;
10410 margin-bottom: 5px;
10353 10411 box-sizing: border-box;
10354 10412 -moz-box-sizing: border-box;
10355 10413 -webkit-box-sizing: border-box;
@@ -10380,8 +10438,8 b' span#autosave_status {'
10380 10438 border: 0px;
10381 10439 min-height: 27px;
10382 10440 margin-left: 32px;
10383 padding-top: 6px;
10384 padding-bottom: 8px;
10441 padding-top: 11px;
10442 padding-bottom: 3px;
10385 10443 }
10386 10444 #maintoolbar .navbar-text {
10387 10445 float: none;
@@ -10391,9 +10449,6 b' span#autosave_status {'
10391 10449 margin-right: 0px;
10392 10450 margin-top: 0px;
10393 10451 }
10394 #maintoolbar .toolbar {
10395 margin-top: 0px;
10396 }
10397 10452 .select-xs {
10398 10453 height: 24px;
10399 10454 }
@@ -8,12 +8,14 b' require(['
8 8 'base/js/events',
9 9 'base/js/page',
10 10 'base/js/utils',
11 'services/config',
11 12 'contents',
12 13 'tree/js/notebooklist',
13 14 'tree/js/clusterlist',
14 15 'tree/js/sessionlist',
15 16 'tree/js/kernellist',
16 17 'tree/js/terminallist',
18 'tree/js/newnotebook',
17 19 'auth/js/loginwidget',
18 20 // only loaded, not used:
19 21 'jqueryui',
@@ -26,12 +28,14 b' require(['
26 28 events,
27 29 page,
28 30 utils,
31 config,
29 32 contents_service,
30 notebooklist,
31 clusterlist,
32 sesssionlist,
33 notebooklist,
34 clusterlist,
35 sesssionlist,
33 36 kernellist,
34 37 terminallist,
38 newnotebook,
35 39 loginwidget){
36 40 "use strict";
37 41
@@ -41,6 +45,10 b' require(['
41 45 base_url: utils.get_body_data("baseUrl"),
42 46 notebook_path: utils.get_body_data("notebookPath"),
43 47 };
48 var cfg = new config.ConfigSection('tree', common_options);
49 cfg.load();
50 common_options.config = cfg;
51
44 52 var session_list = new sesssionlist.SesssionList($.extend({
45 53 events: events},
46 54 common_options));
@@ -63,24 +71,12 b' require(['
63 71
64 72 var login_widget = new loginwidget.LoginWidget('#login_widget', common_options);
65 73
66 $('#new_notebook').click(function (e) {
67 var w = window.open();
68 contents.new_untitled(common_options.notebook_path, {type: "notebook"}).then(
69 function (data) {
70 w.location = utils.url_join_encode(
71 common_options.base_url, 'notebooks', data.path
72 );
73 },
74 function(error) {
75 w.close();
76 dialog.modal({
77 title : 'Creating Notebook Failed',
78 body : "The error was: " + error.message,
79 buttons : {'OK' : {'class' : 'btn-primary'}}
80 });
81 }
82 );
83 });
74 var nnw = new newnotebook.NewNotebookWidget("#new-notebook-buttons",
75 $.extend(
76 {contents: contents},
77 common_options
78 )
79 );
84 80
85 81 var interval_id=0;
86 82 // auto refresh every xx secondes, no need to be fast,
@@ -93,18 +89,18 b' require(['
93 89 */
94 90 session_list.load_sessions();
95 91 cluster_list.load_list();
96 if (terminal_list) {
97 terminal_list.load_terminals();
98 }
92 if (terminal_list) {
93 terminal_list.load_terminals();
94 }
99 95 if (!interval_id){
100 96 interval_id = setInterval(function(){
101 session_list.load_sessions();
102 cluster_list.load_list();
103 if (terminal_list) {
104 terminal_list.load_terminals();
105 }
106 }, time_refresh*1000);
107 }
97 session_list.load_sessions();
98 cluster_list.load_list();
99 if (terminal_list) {
100 terminal_list.load_terminals();
101 }
102 }, time_refresh*1000);
103 }
108 104 };
109 105
110 106 var disable_autorefresh = function(){
@@ -134,6 +130,7 b' require(['
134 130 IPython.session_list = session_list;
135 131 IPython.kernel_list = kernel_list;
136 132 IPython.login_widget = login_widget;
133 IPython.new_notebook_widget = nnw;
137 134
138 135 events.trigger('app_initialized.DashboardApp');
139 136
@@ -15,12 +15,14 b''
15 15
16 16 .alternate_upload input.fileinput
17 17 {
18 background-color:red;
19 position:relative;
18 display: inline;
20 19 opacity: 0;
21 20 z-index: 2;
22 width: 295px;
23 margin-left:163px;
24 cursor: pointer;
25 height: 26px;
21 width: 12ex;
22 margin-right: -12ex;
23 }
24
25 .alternate_upload .input-overlay {
26 display: inline-block;
27 font-weight: bold;
26 28 }
@@ -154,3 +154,23 b' input.engine_num_input {'
154 154 .file_icon:before {
155 155 .icon(@fa-var-file-o)
156 156 }
157
158 ul#new-notebook-menu {
159 // align right instead of left
160 left: auto;
161 right: 0;
162 }
163
164 .kernel-menu-icon {
165 padding-right: 12px;
166 width: 24px;
167 content: @fa-var-square-o;
168 }
169
170 .kernel-menu-icon:before {
171 content: @fa-var-square-o;
172 }
173
174 .kernel-menu-icon-current:before {
175 content: @fa-var-check;
176 }
@@ -112,6 +112,7 b' define(['
112 112 return Promise.resolve(view.render()).then(function() {return view;});
113 113 }).catch(utils.reject("Couldn't create a view for model id '" + String(model.id) + "'", true));
114 114 });
115 model.views[utils.uuid()] = model.state_change;
115 116 return model.state_change;
116 117 };
117 118
@@ -65,11 +65,12 b' define(["widgets/js/manager",'
65 65 delete this.comm.model; // Delete ref so GC will collect widget model.
66 66 delete this.comm;
67 67 delete this.model_id; // Delete id from model so widget manager cleans up.
68 for (var id in this.views) {
69 if (this.views.hasOwnProperty(id)) {
70 this.views[id].remove();
71 }
72 }
68 _.each(this.views, function(v, id, views) {
69 v.then(function(view) {
70 view.remove();
71 delete views[id];
72 });
73 });
73 74 },
74 75
75 76 _handle_comm_msg: function (msg) {
@@ -318,8 +319,6 b' define(["widgets/js/manager",'
318 319 */
319 320 this.model.on('change',this.update,this);
320 321 this.options = parameters.options;
321 this.id = this.id || utils.uuid();
322 this.model.views[this.id] = this;
323 322 this.on('displayed', function() {
324 323 this.is_displayed = true;
325 324 }, this);
@@ -388,7 +387,7 b' define(["widgets/js/manager",'
388 387 } else {
389 388 this.on('displayed', callback, context);
390 389 }
391 },
390 }
392 391 });
393 392
394 393
@@ -575,6 +574,10 b' define(["widgets/js/manager",'
575 574 }
576 575 return elements;
577 576 },
577
578 typeset: function(element, text){
579 utils.typeset.apply(null, arguments);
580 },
578 581 });
579 582
580 583
@@ -62,8 +62,7 b' define(['
62 62 if (description.trim().length === 0) {
63 63 this.$label.hide();
64 64 } else {
65 this.$label.text(description);
66 MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.$label.get(0)]);
65 this.typeset(this.$label, description);
67 66 this.$label.show();
68 67 }
69 68 }
@@ -319,8 +319,7 b' define(['
319 319 if (description.trim().length === 0) {
320 320 this.$title.html("&nbsp;"); // Preserve title height
321 321 } else {
322 this.$title.text(description);
323 MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.$title.get(0)]);
322 this.typeset(this.$title, description);
324 323 }
325 324
326 325 var button_text = this.model.get('button_text');
@@ -155,8 +155,7 b' define(['
155 155 if (description.length === 0) {
156 156 this.$label.hide();
157 157 } else {
158 this.$label.text(description);
159 MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.$label.get(0)]);
158 this.typeset(this.$label, description);
160 159 this.$label.show();
161 160 }
162 161
@@ -323,8 +322,7 b' define(['
323 322 if (description.length === 0) {
324 323 this.$label.hide();
325 324 } else {
326 this.$label.text(description);
327 MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.$label.get(0)]);
325 this.typeset(this.$label, description);
328 326 this.$label.show();
329 327 }
330 328 }
@@ -443,8 +441,7 b' define(['
443 441 if (description.length === 0) {
444 442 this.$label.hide();
445 443 } else {
446 this.$label.text(description);
447 MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.$label.get(0)]);
444 this.typeset(this.$label, description);
448 445 this.$label.show();
449 446 }
450 447 return ProgressView.__super__.update.apply(this);
@@ -97,8 +97,7 b' define(['
97 97 if (description.length === 0) {
98 98 this.$label.hide();
99 99 } else {
100 this.$label.text(description);
101 MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.$label.get(0)]);
100 this.typeset(this.$label, description);
102 101 this.$label.show();
103 102 }
104 103 }
@@ -231,7 +230,7 b' define(['
231 230 this.$label.hide();
232 231 } else {
233 232 this.$label.text(description);
234 MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.$label.get(0)]);
233 this.typeset(this.$label, description);
235 234 this.$label.show();
236 235 }
237 236 }
@@ -345,8 +344,8 b' define(['
345 344 if (description.length === 0) {
346 345 this.$label.hide();
347 346 } else {
348 this.$label.text(description);
349 MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.$label.get(0)]);
347 this.$label.text();
348 this.typeset(this.$label, description);
350 349 this.$label.show();
351 350 }
352 351 }
@@ -468,8 +467,7 b' define(['
468 467 if (description.length === 0) {
469 468 this.$label.hide();
470 469 } else {
471 this.$label.text(description);
472 MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.$label.get(0)]);
470 this.typeset(this.$label, description);
473 471 this.$label.show();
474 472 }
475 473 }
@@ -43,9 +43,7 b' define(['
43 43 * Called when the model is changed. The model may have been
44 44 * changed by another view or by a state update from the back-end.
45 45 */
46 this.$el.text(this.model.get('value'));
47 MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.$el.get(0)]);
48
46 this.typeset(this.$el, this.model.get('value'));
49 47 return LatexView.__super__.update.apply(this);
50 48 },
51 49 });
@@ -116,8 +114,7 b' define(['
116 114 if (description.length === 0) {
117 115 this.$label.hide();
118 116 } else {
119 this.$label.text(description);
120 MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.$label.get(0)]);
117 this.typeset(this.$label, description);
121 118 this.$label.show();
122 119 }
123 120 }
@@ -200,8 +197,7 b' define(['
200 197 if (description.length === 0) {
201 198 this.$label.hide();
202 199 } else {
203 this.$label.text(description);
204 MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.$label.get(0)]);
200 this.typeset(this.$label, description);
205 201 this.$label.show();
206 202 }
207 203 }
@@ -32,7 +32,7 b' class="notebook_app"'
32 32 {% endblock %}
33 33
34 34
35 {% block header %}
35 {% block headercontainer %}
36 36
37 37
38 38 <span id="save_widget" class="nav pull-left">
@@ -43,18 +43,16 b' class="notebook_app"'
43 43
44 44 <span id="kernel_selector_widget" class="pull-right dropdown">
45 45 <button class="dropdown-toggle" data-toggle="dropdown" type='button' id="current_kernel_spec">
46 <span class='kernel_name'>Python</span>
46 <span class='kernel_name'>Kernel</span>
47 47 <span class="caret"></span>
48 48 </button>
49 49 <ul id="kernel_selector" class="dropdown-menu">
50 50 </ul>
51 51 </span>
52 52
53 {% endblock %}
54
55
56 {% block site %}
53 {% endblock headercontainer %}
57 54
55 {% block header %}
58 56 <div id="menubar-container" class="container">
59 57 <div id="menubar">
60 58 <div id="menus" class="navbar navbar-default" role="navigation">
@@ -292,6 +290,7 b' class="notebook_app"'
292 290 </div>
293 291 </div>
294 292 </div>
293
295 294 <div id="maintoolbar" class="navbar">
296 295 <div class="toolbar-inner navbar-inner navbar-nobg">
297 296 <div id="maintoolbar-container" class="container"></div>
@@ -299,19 +298,25 b' class="notebook_app"'
299 298 </div>
300 299 </div>
301 300
302 <div id="ipython-main-app">
301 <div class="lower-header-bar"></div>
302 {% endblock header %}
303 303
304 {% block site %}
305
306
307 <div id="ipython-main-app">
304 308 <div id="notebook_panel">
305 309 <div id="notebook"></div>
306 <div id="pager_splitter"></div>
307 <div id="pager">
308 <div id='pager_button_area'>
309 </div>
310 <div id="pager-container" class="container"></div>
311 </div>
312 310 </div>
311 </div>
313 312
313 <div id="pager">
314 <div id="pager-contents">
315 <div id="pager-container" class="container"></div>
316 </div>
317 <div id='pager-button-area'></div>
314 318 </div>
319
315 320 <div id='tooltip' class='ipython_tooltip' style='display:none'></div>
316 321
317 322
@@ -5,7 +5,7 b''
5 5 <meta charset="utf-8">
6 6
7 7 <title>{% block title %}IPython Notebook{% endblock %}</title>
8 <link rel="shortcut icon" type="image/x-icon" href="{{static_url("base/images/favicon.ico") }}">
8 {% block favicon %}<link rel="shortcut icon" type="image/x-icon" href="{{static_url("base/images/favicon.ico") }}">{% endblock %}
9 9 <meta http-equiv="X-UA-Compatible" content="chrome=1">
10 10 <link rel="stylesheet" href="{{static_url("components/jquery-ui/themes/smoothness/jquery-ui.min.css") }}" type="text/css" />
11 11 <meta name="viewport" content="width=device-width, initial-scale=1.0">
@@ -81,7 +81,7 b''
81 81 </noscript>
82 82
83 83 <div id="header" class="navbar navbar-static-top">
84 <div class="container">
84 <div id="header-container" class="container">
85 85 <div id="ipython_notebook" class="nav navbar-brand pull-left"><a href="{{base_url}}tree" alt='dashboard'>{% block logo %}<img src='{{static_url("base/images/logo.png") }}' alt='Jupyter Notebook'/>{% endblock %}</a></div>
86 86
87 87 {% block login_widget %}
@@ -96,10 +96,15 b''
96 96
97 97 {% endblock %}
98 98
99 {% block header %}
99 {% block headercontainer %}
100 100 {% endblock %}
101 101 </div>
102 <div class="header-bar"></div>
103
104 {% block header %}
105 {% endblock %}
102 106 </div>
107 <div id="header-spacer"></div>
103 108
104 109 <div id="site">
105 110 {% block site %}
@@ -34,19 +34,31 b' data-terminals-available="{{terminals_available}}"'
34 34 <div class="tab-content">
35 35 <div id="notebooks" class="tab-pane active">
36 36 <div id="notebook_toolbar" class="row">
37 <div class="col-sm-8 no-padding">
38 <form id='alternate_upload' class='alternate_upload' >
39 <span id="notebook_list_info" style="position:absolute" >
40 To import a notebook, drag the file onto the listing below or <strong>click here</strong>.
37 <div class="col-sm-12 no-padding">
38 <form id='alternate_upload' class='alternate_upload'>
39 <span id="notebook_list_info">
40 To import a notebook, drag the file onto the listing below or
41 <span class="input-overlay">
42 <input type="file" name="datafile" class="fileinput" multiple='multiple'>
43 click here.
44 </span>
41 45 </span>
42 <input type="file" name="datafile" class="fileinput" multiple='multiple'>
43 46 </form>
44 </div>
45 <div class="col-sm-4 no-padding tree-buttons">
46 <span id="notebook_buttons" class="pull-right">
47 <button id="new_notebook" title="Create new notebook" class="btn btn-default btn-xs">New Notebook</button>
48 <button id="refresh_notebook_list" title="Refresh notebook list" class="btn btn-default btn-xs"><i class="fa fa-refresh"></i></button>
49 </span>
47 <span id="notebook_buttons" class="pull-right">
48 <div id="new-notebook-buttons" class="btn-group">
49 <button id="new_notebook" class="btn btn-default btn-xs">
50 New Notebook
51 </button>
52 <button class="dropdown-toggle btn btn-default btn-xs" data-toggle="dropdown">
53 <span id="current-kernel">Loading...</span>
54 <span class="caret"></span>
55 </button>
56 <ul id="new-notebook-menu" class="dropdown-menu"></ul>
57 </div>
58
59
60 <button id="refresh_notebook_list" title="Refresh notebook list" class="btn btn-default btn-xs"><i class="fa fa-refresh"></i></button>
61 </span>
50 62 </div>
51 63 </div>
52 64
@@ -22,3 +22,8 b' from .widget_int import IntTextWidget, BoundedIntTextWidget, IntSliderWidget, In'
22 22 from .widget_selection import RadioButtonsWidget, ToggleButtonsWidget, DropdownWidget, SelectWidget
23 23 from .widget_selectioncontainer import TabWidget, AccordionWidget
24 24 from .widget_string import HTMLWidget, LatexWidget, TextWidget, TextareaWidget
25
26 # Warn on import
27 from IPython.utils.warn import warn
28 warn("""The widget API is still considered experimental and
29 may change by the next major release of IPython.""")
@@ -562,7 +562,7 b' class ConnectionFileMixin(LoggingConfigurable):'
562 562 return self._create_connected_socket('hb', identity=identity)
563 563
564 564 def connect_control(self, identity=None):
565 """return zmq Socket connected to the Heartbeat channel"""
565 """return zmq Socket connected to the Control channel"""
566 566 return self._create_connected_socket('control', identity=identity)
567 567
568 568
@@ -101,25 +101,13 b" def make_ipkernel_cmd(mod='IPython.kernel', executable=None, extra_arguments=[],"
101 101 executable = sys.executable
102 102 arguments = [ executable, '-m', mod, '-f', '{connection_file}' ]
103 103 arguments.extend(extra_arguments)
104
105 if sys.platform == 'win32':
106
107 # If the kernel is running on pythonw and stdout/stderr are not been
108 # re-directed, it will crash when more than 4KB of data is written to
109 # stdout or stderr. This is a bug that has been with Python for a very
110 # long time; see http://bugs.python.org/issue706263.
111 # A cleaner solution to this problem would be to pass os.devnull to
112 # Popen directly. Unfortunately, that does not work.
113 if executable.endswith('pythonw.exe'):
114 arguments.append('--no-stdout')
115 arguments.append('--no-stderr')
116
104
117 105 return arguments
118 106
119 107
120 108 def launch_kernel(cmd, stdin=None, stdout=None, stderr=None, env=None,
121 109 independent=False,
122 cwd=None, ipython_kernel=True,
110 cwd=None,
123 111 **kw
124 112 ):
125 113 """ Launches a localhost kernel, binding to the specified ports.
@@ -141,10 +129,6 b' def launch_kernel(cmd, stdin=None, stdout=None, stderr=None, env=None,'
141 129 cwd : path, optional
142 130 The working dir of the kernel process (default: cwd of this process).
143 131
144 ipython_kernel : bool, optional
145 Whether the kernel is an official IPython one,
146 and should get a bit of special treatment.
147
148 132 Returns
149 133 -------
150 134
@@ -166,14 +150,22 b' def launch_kernel(cmd, stdin=None, stdout=None, stderr=None, env=None,'
166 150 # stderr are all invalid.
167 151 redirect_out = sys.executable.endswith('pythonw.exe')
168 152 if redirect_out:
169 _stdout = PIPE if stdout is None else stdout
170 _stderr = PIPE if stderr is None else stderr
153 blackhole = open(os.devnull, 'w')
154 _stdout = blackhole if stdout is None else stdout
155 _stderr = blackhole if stderr is None else stderr
171 156 else:
172 157 _stdout, _stderr = stdout, stderr
173 158
174 159 env = env if (env is not None) else os.environ.copy()
175 160
176 161 encoding = getdefaultencoding(prefer_stream=False)
162 kwargs = dict(
163 stdin=_stdin,
164 stdout=_stdout,
165 stderr=_stderr,
166 cwd=cwd,
167 env=env,
168 )
177 169
178 170 # Spawn a kernel.
179 171 if sys.platform == 'win32':
@@ -181,73 +173,49 b' def launch_kernel(cmd, stdin=None, stdout=None, stderr=None, env=None,'
181 173 cmd = [ cast_bytes_py2(c, encoding) for c in cmd ]
182 174 if cwd:
183 175 cwd = cast_bytes_py2(cwd, sys.getfilesystemencoding() or 'ascii')
176 kwargs['cwd'] = cwd
184 177
185 178 from IPython.kernel.zmq.parentpoller import ParentPollerWindows
186 # Create a Win32 event for interrupting the kernel.
179 # Create a Win32 event for interrupting the kernel
180 # and store it in an environment variable.
187 181 interrupt_event = ParentPollerWindows.create_interrupt_event()
188 # Store this in an environment variable for third party kernels, but at
189 # present, our own kernel expects this as a command line argument.
190 env["IPY_INTERRUPT_EVENT"] = str(interrupt_event)
191 if ipython_kernel:
192 cmd += [ '--interrupt=%i' % interrupt_event ]
193
194 # If the kernel is running on pythonw and stdout/stderr are not been
195 # re-directed, it will crash when more than 4KB of data is written to
196 # stdout or stderr. This is a bug that has been with Python for a very
197 # long time; see http://bugs.python.org/issue706263.
198 # A cleaner solution to this problem would be to pass os.devnull to
199 # Popen directly. Unfortunately, that does not work.
200 if cmd[0].endswith('pythonw.exe'):
201 if stdout is None:
202 cmd.append('--no-stdout')
203 if stderr is None:
204 cmd.append('--no-stderr')
205
206 # Launch the kernel process.
182 env["JPY_INTERRUPT_EVENT"] = str(interrupt_event)
183 # deprecated old env name:
184 env["IPY_INTERRUPT_EVENT"] = env["JPY_INTERRUPT_EVENT"]
185
186 try:
187 from _winapi import DuplicateHandle, GetCurrentProcess, \
188 DUPLICATE_SAME_ACCESS, CREATE_NEW_PROCESS_GROUP
189 except:
190 from _subprocess import DuplicateHandle, GetCurrentProcess, \
191 DUPLICATE_SAME_ACCESS, CREATE_NEW_PROCESS_GROUP
192 # Launch the kernel process
207 193 if independent:
208 proc = Popen(cmd,
209 creationflags=512, # CREATE_NEW_PROCESS_GROUP
210 stdin=_stdin, stdout=_stdout, stderr=_stderr, env=env)
194 kwargs['creationflags'] = CREATE_NEW_PROCESS_GROUP
211 195 else:
212 if ipython_kernel:
213 try:
214 from _winapi import DuplicateHandle, GetCurrentProcess, \
215 DUPLICATE_SAME_ACCESS
216 except:
217 from _subprocess import DuplicateHandle, GetCurrentProcess, \
218 DUPLICATE_SAME_ACCESS
219 pid = GetCurrentProcess()
220 handle = DuplicateHandle(pid, pid, pid, 0,
221 True, # Inheritable by new processes.
222 DUPLICATE_SAME_ACCESS)
223 cmd +=[ '--parent=%i' % handle ]
224
225
226 proc = Popen(cmd,
227 stdin=_stdin, stdout=_stdout, stderr=_stderr, cwd=cwd, env=env)
196 pid = GetCurrentProcess()
197 handle = DuplicateHandle(pid, pid, pid, 0,
198 True, # Inheritable by new processes.
199 DUPLICATE_SAME_ACCESS)
200 env['JPY_PARENT_PID'] = str(handle)
201
202 proc = Popen(cmd, **kwargs)
228 203
229 204 # Attach the interrupt event to the Popen objet so it can be used later.
230 205 proc.win32_interrupt_event = interrupt_event
231 206
232 207 else:
233 208 if independent:
234 proc = Popen(cmd, preexec_fn=lambda: os.setsid(),
235 stdin=_stdin, stdout=_stdout, stderr=_stderr, cwd=cwd, env=env)
209 kwargs['preexec_fn'] = lambda: os.setsid()
236 210 else:
237 if ipython_kernel:
238 cmd += ['--parent=1']
239 proc = Popen(cmd,
240 stdin=_stdin, stdout=_stdout, stderr=_stderr, cwd=cwd, env=env)
211 env['JPY_PARENT_PID'] = str(os.getpid())
212
213 proc = Popen(cmd, **kwargs)
241 214
242 215 # Clean up pipes created to work around Popen bug.
243 216 if redirect_in:
244 217 if stdin is None:
245 218 proc.stdin.close()
246 if redirect_out:
247 if stdout is None:
248 proc.stdout.close()
249 if stderr is None:
250 proc.stderr.close()
251 219
252 220 return proc
253 221
@@ -237,7 +237,6 b' class KernelManager(ConnectionFileMixin):'
237 237 env.update(self.kernel_spec.env or {})
238 238 # launch the kernel subprocess
239 239 self.kernel = self._launch_kernel(kernel_cmd, env=env,
240 ipython_kernel=self.ipython_kernel,
241 240 **kw)
242 241 self.start_restarter()
243 242 self._connect_control_socket()
@@ -53,11 +53,8 b' kernel_aliases.update({'
53 53 'stdin' : 'IPKernelApp.stdin_port',
54 54 'control' : 'IPKernelApp.control_port',
55 55 'f' : 'IPKernelApp.connection_file',
56 'parent': 'IPKernelApp.parent_handle',
57 56 'transport': 'IPKernelApp.transport',
58 57 })
59 if sys.platform.startswith('win'):
60 kernel_aliases['interrupt'] = 'IPKernelApp.interrupt'
61 58
62 59 kernel_flags = dict(base_flags)
63 60 kernel_flags.update({
@@ -133,11 +130,11 b' class IPKernelApp(BaseIPythonApplication, InteractiveShellApp,'
133 130 config=True, help="The importstring for the DisplayHook factory")
134 131
135 132 # polling
136 parent_handle = Integer(0, config=True,
133 parent_handle = Integer(int(os.environ.get('JPY_PARENT_PID') or 0), config=True,
137 134 help="""kill this process if its parent dies. On Windows, the argument
138 135 specifies the HANDLE of the parent process, otherwise it is simply boolean.
139 136 """)
140 interrupt = Integer(0, config=True,
137 interrupt = Integer(int(os.environ.get('JPY_INTERRUPT_EVENT') or 0), config=True,
141 138 help="""ONLY USED ON WINDOWS
142 139 Interrupt this process when the parent is signaled.
143 140 """)
@@ -11,14 +11,14 b' except:'
11 11 import pickle
12 12
13 13 # IPython imports
14 from IPython.utils import py3compat
14 from IPython.utils.py3compat import PY3, buffer_to_bytes_py2
15 15 from IPython.utils.data import flatten
16 16 from IPython.utils.pickleutil import (
17 17 can, uncan, can_sequence, uncan_sequence, CannedObject,
18 18 istype, sequence_types, PICKLE_PROTOCOL,
19 19 )
20 20
21 if py3compat.PY3:
21 if PY3:
22 22 buffer = memoryview
23 23
24 24 #-----------------------------------------------------------------------------
@@ -105,10 +105,7 b' def deserialize_object(buffers, g=None):'
105 105 (newobj, bufs) : unpacked object, and the list of remaining unused buffers.
106 106 """
107 107 bufs = list(buffers)
108 pobj = bufs.pop(0)
109 if not isinstance(pobj, bytes):
110 # a zmq message
111 pobj = bytes(pobj)
108 pobj = buffer_to_bytes_py2(bufs.pop(0))
112 109 canned = pickle.loads(pobj)
113 110 if istype(canned, sequence_types) and len(canned) < MAX_ITEMS:
114 111 for c in canned:
@@ -161,11 +158,10 b' def unpack_apply_message(bufs, g=None, copy=True):'
161 158 Returns: original f,args,kwargs"""
162 159 bufs = list(bufs) # allow us to pop
163 160 assert len(bufs) >= 2, "not enough buffers!"
164 if not copy:
165 for i in range(2):
166 bufs[i] = bufs[i].bytes
167 f = uncan(pickle.loads(bufs.pop(0)), g)
168 info = pickle.loads(bufs.pop(0))
161 pf = buffer_to_bytes_py2(bufs.pop(0))
162 f = uncan(pickle.loads(pf), g)
163 pinfo = buffer_to_bytes_py2(bufs.pop(0))
164 info = pickle.loads(pinfo)
169 165 arg_bufs, kwarg_bufs = bufs[:info['narg_bufs']], bufs[info['narg_bufs']:]
170 166
171 167 args = []
@@ -447,12 +447,13 b' class ZMQInteractiveShell(InteractiveShell):'
447 447
448 448 return exc_content
449 449
450 def set_next_input(self, text):
450 def set_next_input(self, text, replace=False):
451 451 """Send the specified text to the frontend to be presented at the next
452 452 input cell."""
453 453 payload = dict(
454 454 source='set_next_input',
455 text=text
455 text=text,
456 replace=replace,
456 457 )
457 458 self.payload_manager.write_payload(payload)
458 459
@@ -1,14 +1,33 b''
1 1 """Generic script exporter class for any kernel language"""
2 2
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
5
3 6 from .templateexporter import TemplateExporter
4 7
8 from IPython.utils.traitlets import Dict
9
5 10 class ScriptExporter(TemplateExporter):
11
12 _exporters = Dict()
13
6 14 def _template_file_default(self):
7 15 return 'script'
8 16
9 17 def from_notebook_node(self, nb, resources=None, **kw):
10 18 langinfo = nb.metadata.get('language_info', {})
19
20 # delegate to custom exporter, if specified
21 exporter_name = langinfo.get('nbconvert_exporter')
22 if exporter_name and exporter_name != 'script':
23 self.log.debug("Loading script exporter: %s", exporter_name)
24 from .export import exporter_map
25 if exporter_name not in self._exporters:
26 Exporter = exporter_map[exporter_name]
27 self._exporters[exporter_name] = Exporter(parent=self)
28 exporter = self._exporters[exporter_name]
29 return exporter.from_notebook_node(nb, resources, **kw)
30
11 31 self.file_extension = langinfo.get('file_extension', '.txt')
12 32 self.output_mimetype = langinfo.get('mimetype', 'text/plain')
13
14 33 return super(ScriptExporter, self).from_notebook_node(nb, resources, **kw)
@@ -77,7 +77,7 b' class MathBlockGrammar(mistune.BlockGrammar):'
77 77
78 78 @undoc
79 79 class MathBlockLexer(mistune.BlockLexer):
80 default_features = ['block_math', 'latex_environment'] + mistune.BlockLexer.default_features
80 default_rules = ['block_math', 'latex_environment'] + mistune.BlockLexer.default_rules
81 81
82 82 def __init__(self, rules=None, **kwargs):
83 83 if rules is None:
@@ -105,7 +105,7 b' class MathInlineGrammar(mistune.InlineGrammar):'
105 105
106 106 @undoc
107 107 class MathInlineLexer(mistune.InlineLexer):
108 default_features = ['math'] + mistune.InlineLexer.default_features
108 default_rules = ['math'] + mistune.InlineLexer.default_rules
109 109
110 110 def __init__(self, renderer, rules=None, **kwargs):
111 111 if rules is None:
@@ -124,10 +124,10 b' class MarkdownWithMath(mistune.Markdown):'
124 124 kwargs['block'] = MathBlockLexer
125 125 super(MarkdownWithMath, self).__init__(renderer, **kwargs)
126 126
127 def parse_block_math(self):
127 def output_block_math(self):
128 128 return self.renderer.block_math(self.token['text'])
129 129
130 def parse_latex_environment(self):
130 def output_latex_environment(self):
131 131 return self.renderer.latex_environment(self.token['name'], self.token['text'])
132 132
133 133 @undoc
@@ -5,6 +5,8 b' Use this module to read or write notebook files as particular nbformat versions.'
5 5
6 6 # Copyright (c) IPython Development Team.
7 7 # Distributed under the terms of the Modified BSD License.
8 import io
9 from IPython.utils import py3compat
8 10
9 11 from IPython.utils.log import get_logger
10 12
@@ -112,8 +114,8 b' def read(fp, as_version, **kwargs):'
112 114
113 115 Parameters
114 116 ----------
115 fp : file
116 Any file-like object with a read method.
117 fp : file or str
118 Any file-like object with a read method, or a path to a file.
117 119 as_version: int
118 120 The version of the notebook format to return.
119 121 The notebook will be converted, if necessary.
@@ -124,6 +126,10 b' def read(fp, as_version, **kwargs):'
124 126 nb : NotebookNode
125 127 The notebook that was read.
126 128 """
129 if isinstance(fp, py3compat.string_types):
130 with io.open(fp, encoding='utf-8') as f:
131 return read(f, as_version, **kwargs)
132
127 133 return reads(fp.read(), as_version, **kwargs)
128 134
129 135
@@ -136,14 +142,19 b' def write(nb, fp, version=NO_CONVERT, **kwargs):'
136 142 ----------
137 143 nb : NotebookNode
138 144 The notebook to write.
139 fp : file
140 Any file-like object with a write method that accepts unicode.
145 fp : file or str
146 Any file-like object with a write method that accepts unicode, or
147 a path to write a file.
141 148 version : int, optional
142 149 The nbformat version to write.
143 150 If nb is not this version, it will be converted.
144 151 If unspecified, or specified as nbformat.NO_CONVERT,
145 152 the notebook's own version will be used and no conversion performed.
146 153 """
154 if isinstance(fp, py3compat.string_types):
155 with io.open(fp, 'w', encoding='utf-8') as f:
156 return write(nb, f, version=version, **kwargs)
157
147 158 s = writes(nb, version, **kwargs)
148 159 if isinstance(s, bytes):
149 160 s = s.decode('utf8')
@@ -4,11 +4,13 b''
4 4 # Distributed under the terms of the Modified BSD License.
5 5
6 6 import json
7 import os
7 8
8 9 from .base import TestsBase
9 10
11 from IPython.utils.tempdir import TemporaryDirectory
10 12 from ..reader import get_version
11 from IPython.nbformat import read, current_nbformat, writes
13 from IPython.nbformat import read, current_nbformat, writes, write
12 14
13 15
14 16 class TestAPI(TestsBase):
@@ -35,3 +37,13 b' class TestAPI(TestsBase):'
35 37 nb2 = json.loads(jsons)
36 38 (major, minor) = get_version(nb2)
37 39 self.assertEqual(major, 2)
40
41 def test_read_write_path(self):
42 """read() and write() take filesystem paths"""
43 path = os.path.join(self._get_files_path(), u'test4.ipynb')
44 nb = read(path, as_version=4)
45
46 with TemporaryDirectory() as td:
47 dest = os.path.join(td, 'echidna.ipynb')
48 write(nb, dest)
49 assert os.path.isfile(dest)
@@ -615,10 +615,11 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
615 615
616 616 if clear:
617 617 self._control.clear()
618 self._append_plain_text(self.banner)
619 if self.kernel_banner:
620 self._append_plain_text(self.kernel_banner)
621
618 if self._display_banner:
619 self._append_plain_text(self.banner)
620 if self.kernel_banner:
621 self._append_plain_text(self.kernel_banner)
622
622 623 # update output marker for stdout/stderr, so that startup
623 624 # messages appear after banner:
624 625 self._append_before_prompt_pos = self._get_cursor().position()
@@ -50,6 +50,7 b" if os.name == 'nt':"
50 50 from IPython.external.qt import QtCore, QtGui
51 51
52 52 # Local imports
53 from IPython.config.application import boolean_flag
53 54 from IPython.config.application import catch_config_error
54 55 from IPython.core.application import BaseIPythonApplication
55 56 from IPython.qt.console.ipython_widget import IPythonWidget
@@ -91,6 +92,11 b' qt_flags = {'
91 92 'plain' : ({'IPythonQtConsoleApp' : {'plain' : True}},
92 93 "Disable rich text support."),
93 94 }
95 qt_flags.update(boolean_flag(
96 'banner', 'IPythonQtConsoleApp.display_banner',
97 "Display a banner upon starting the QtConsole.",
98 "Don't display a banner upon starting the QtConsole."
99 ))
94 100
95 101 # and app_flags from the Console Mixin
96 102 qt_flags.update(app_flags)
@@ -168,6 +174,10 b' class IPythonQtConsoleApp(BaseIPythonApplication, IPythonConsoleApp):'
168 174 plain = CBool(False, config=True,
169 175 help="Use a plaintext widget instead of rich text (plain can't print/save).")
170 176
177 display_banner = CBool(True, config=True,
178 help="Whether to display a banner upon starting the QtConsole."
179 )
180
171 181 def _plain_changed(self, name, old, new):
172 182 kind = 'plain' if new else 'rich'
173 183 self.config.ConsoleWidget.kind = kind
@@ -255,6 +265,7 b' class IPythonQtConsoleApp(BaseIPythonApplication, IPythonConsoleApp):'
255 265 self.widget._existing = self.existing
256 266 self.widget._may_close = not self.existing
257 267 self.widget._confirm_exit = self.confirm_exit
268 self.widget._display_banner = self.display_banner
258 269
259 270 self.widget.kernel_manager = self.kernel_manager
260 271 self.widget.kernel_client = self.kernel_client
@@ -17,10 +17,6 b' class ChannelQObject(SuperQObject):'
17 17 # Emitted when the channel is stopped.
18 18 stopped = QtCore.Signal()
19 19
20 #---------------------------------------------------------------------------
21 # Channel interface
22 #---------------------------------------------------------------------------
23
24 20 def start(self):
25 21 """ Reimplemented to emit signal.
26 22 """
@@ -61,10 +57,6 b' class QtShellChannelMixin(ChannelQObject):'
61 57 history_reply = QtCore.Signal(object)
62 58 kernel_info_reply = QtCore.Signal(object)
63 59
64 #---------------------------------------------------------------------------
65 # 'ShellChannel' interface
66 #---------------------------------------------------------------------------
67
68 60 def call_handlers(self, msg):
69 61 """ Reimplemented to emit signals instead of making callbacks.
70 62 """
@@ -108,10 +100,6 b' class QtIOPubChannelMixin(ChannelQObject):'
108 100 # Emitted when a shutdown is noticed.
109 101 shutdown_reply_received = QtCore.Signal(object)
110 102
111 #---------------------------------------------------------------------------
112 # 'IOPubChannel' interface
113 #---------------------------------------------------------------------------
114
115 103 def call_handlers(self, msg):
116 104 """ Reimplemented to emit signals instead of making callbacks.
117 105 """
@@ -122,8 +110,6 b' class QtIOPubChannelMixin(ChannelQObject):'
122 110 signal = getattr(self, msg_type + '_received', None)
123 111 if signal:
124 112 signal.emit(msg)
125 elif msg_type in ('stdout', 'stderr'):
126 self.stream_received.emit(msg)
127 113
128 114 def flush(self):
129 115 """ Reimplemented to ensure that signals are dispatched immediately.
@@ -140,10 +126,6 b' class QtStdInChannelMixin(ChannelQObject):'
140 126 # Emitted when an input request is received.
141 127 input_requested = QtCore.Signal(object)
142 128
143 #---------------------------------------------------------------------------
144 # 'StdInChannel' interface
145 #---------------------------------------------------------------------------
146
147 129 def call_handlers(self, msg):
148 130 """ Reimplemented to emit signals instead of making callbacks.
149 131 """
@@ -161,14 +143,9 b' class QtHBChannelMixin(ChannelQObject):'
161 143 # Emitted when the kernel has died.
162 144 kernel_died = QtCore.Signal(object)
163 145
164 #---------------------------------------------------------------------------
165 # 'HBChannel' interface
166 #---------------------------------------------------------------------------
167
168 146 def call_handlers(self, since_last_heartbeat):
169 147 """ Reimplemented to emit signals instead of making callbacks.
170 148 """
171 # Emit the generic signal.
172 149 self.kernel_died.emit(since_last_heartbeat)
173 150
174 151
@@ -529,7 +529,7 b' class ZMQTerminalInteractiveShell(TerminalInteractiveShell):'
529 529 except KeyboardInterrupt:
530 530 #double-guard against keyboardinterrupts during kbdint handling
531 531 try:
532 self.write('\nKeyboardInterrupt\n')
532 self.write('\n' + self.get_exception_only())
533 533 source_raw = self.input_splitter.raw_reset()
534 534 hlen_b4_cell = self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
535 535 more = False
@@ -468,7 +468,7 b' class TerminalInteractiveShell(InteractiveShell):'
468 468 except KeyboardInterrupt:
469 469 #double-guard against keyboardinterrupts during kbdint handling
470 470 try:
471 self.write('\nKeyboardInterrupt\n')
471 self.write('\n' + self.get_exception_only())
472 472 source_raw = self.input_splitter.raw_reset()
473 473 hlen_b4_cell = \
474 474 self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
@@ -15,6 +15,7 b' import argparse'
15 15 import json
16 16 import multiprocessing.pool
17 17 import os
18 import stat
18 19 import re
19 20 import requests
20 21 import shutil
@@ -170,6 +171,18 b' class PyTestController(TestController):'
170 171 # This means we won't get odd effects from our own matplotlib config
171 172 self.env['MPLCONFIGDIR'] = workingdir.name
172 173
174 # Add a non-accessible directory to PATH (see gh-7053)
175 noaccess = os.path.join(self.workingdir.name, "_no_access_")
176 self.noaccess = noaccess
177 os.mkdir(noaccess, 0)
178
179 PATH = os.environ.get('PATH', '')
180 if PATH:
181 PATH = noaccess + os.pathsep + PATH
182 else:
183 PATH = noaccess
184 self.env['PATH'] = PATH
185
173 186 # From options:
174 187 if self.options.xunit:
175 188 self.add_xunit()
@@ -178,6 +191,14 b' class PyTestController(TestController):'
178 191 self.env['IPTEST_SUBPROC_STREAMS'] = self.options.subproc_streams
179 192 self.cmd.extend(self.options.extra_args)
180 193
194 def cleanup(self):
195 """
196 Make the non-accessible directory created in setup() accessible
197 again, otherwise deleting the workingdir will fail.
198 """
199 os.chmod(self.noaccess, stat.S_IRWXU)
200 TestController.cleanup(self)
201
181 202 @property
182 203 def will_run(self):
183 204 try:
@@ -17,7 +17,7 b' except ImportError:'
17 17 from . import codeutil # This registers a hook when it's imported
18 18 from . import py3compat
19 19 from .importstring import import_item
20 from .py3compat import string_types, iteritems
20 from .py3compat import string_types, iteritems, buffer_to_bytes_py2
21 21
22 22 from IPython.config import Application
23 23 from IPython.utils.log import get_logger
@@ -260,8 +260,8 b' class CannedArray(CannedObject):'
260 260 from numpy import frombuffer
261 261 data = self.buffers[0]
262 262 if self.pickled:
263 # no shape, we just pickled it
264 return pickle.loads(data)
263 # we just pickled it
264 return pickle.loads(buffer_to_bytes_py2(data))
265 265 else:
266 266 return frombuffer(data, dtype=self.dtype).reshape(self.shape)
267 267
@@ -30,6 +30,12 b' def cast_bytes(s, encoding=None):'
30 30 return encode(s, encoding)
31 31 return s
32 32
33 def buffer_to_bytes(buf):
34 """Cast a buffer object to bytes"""
35 if not isinstance(buf, bytes):
36 buf = bytes(buf)
37 return buf
38
33 39 def _modify_str_or_docstring(str_change_func):
34 40 @functools.wraps(str_change_func)
35 41 def wrapper(func_or_str):
@@ -86,6 +92,7 b' if sys.version_info[0] >= 3:'
86 92 bytes_to_str = decode
87 93 cast_bytes_py2 = no_code
88 94 cast_unicode_py2 = no_code
95 buffer_to_bytes_py2 = no_code
89 96
90 97 string_types = (str,)
91 98 unicode_type = str
@@ -151,6 +158,7 b' else:'
151 158 bytes_to_str = no_code
152 159 cast_bytes_py2 = cast_bytes
153 160 cast_unicode_py2 = cast_unicode
161 buffer_to_bytes_py2 = buffer_to_bytes
154 162
155 163 string_types = (str, unicode)
156 164 unicode_type = unicode
@@ -429,6 +429,9 b' The main example being ``%load``.'
429 429 "source": "set_next_input",
430 430 # the text contents of the cell to create
431 431 "text": "some cell content",
432 # If true, replace the current cell in document UIs instead of inserting
433 # a cell. Ignored in console UIs.
434 "replace": bool,
432 435 }
433 436
434 437 **edit**: open a file for editing.
@@ -270,7 +270,7 b' extras_require = dict('
270 270 test = ['nose>=0.10.1', 'requests'],
271 271 terminal = [],
272 272 nbformat = ['jsonschema>=2.0'],
273 notebook = ['tornado>=4.0', 'pyzmq>=2.1.11', 'jinja2', 'pygments', 'mistune>=0.3.1'],
273 notebook = ['tornado>=4.0', 'pyzmq>=2.1.11', 'jinja2', 'pygments', 'mistune>=0.5'],
274 274 nbconvert = ['pygments', 'jinja2', 'mistune>=0.3.1']
275 275 )
276 276
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now