Show More
@@ -0,0 +1,118 b'' | |||||
|
1 | """Markdown filters with mistune | |||
|
2 | ||||
|
3 | Used from markdown.py | |||
|
4 | """ | |||
|
5 | # Copyright (c) IPython Development Team. | |||
|
6 | # Distributed under the terms of the Modified BSD License. | |||
|
7 | ||||
|
8 | from __future__ import print_function | |||
|
9 | ||||
|
10 | import re | |||
|
11 | ||||
|
12 | import mistune | |||
|
13 | ||||
|
14 | from pygments import highlight | |||
|
15 | from pygments.lexers import get_lexer_by_name | |||
|
16 | from pygments.formatters import HtmlFormatter | |||
|
17 | from pygments.util import ClassNotFound | |||
|
18 | ||||
|
19 | from IPython.nbconvert.filters.strings import add_anchor | |||
|
20 | from IPython.nbconvert.utils.exceptions import ConversionException | |||
|
21 | from IPython.utils.decorators import undoc | |||
|
22 | ||||
|
23 | ||||
|
24 | @undoc | |||
|
25 | class MathBlockGrammar(mistune.BlockGrammar): | |||
|
26 | block_math = re.compile("^\$\$(.*?)\$\$", re.DOTALL) | |||
|
27 | latex_environment = re.compile(r"^\\begin\{([a-z]*\*?)\}(.*?)\\end\{\1\}", | |||
|
28 | re.DOTALL) | |||
|
29 | ||||
|
30 | @undoc | |||
|
31 | class MathBlockLexer(mistune.BlockLexer): | |||
|
32 | default_rules = ['block_math', 'latex_environment'] + mistune.BlockLexer.default_rules | |||
|
33 | ||||
|
34 | def __init__(self, rules=None, **kwargs): | |||
|
35 | if rules is None: | |||
|
36 | rules = MathBlockGrammar() | |||
|
37 | super(MathBlockLexer, self).__init__(rules, **kwargs) | |||
|
38 | ||||
|
39 | def parse_block_math(self, m): | |||
|
40 | """Parse a $$math$$ block""" | |||
|
41 | self.tokens.append({ | |||
|
42 | 'type': 'block_math', | |||
|
43 | 'text': m.group(1) | |||
|
44 | }) | |||
|
45 | ||||
|
46 | def parse_latex_environment(self, m): | |||
|
47 | self.tokens.append({ | |||
|
48 | 'type': 'latex_environment', | |||
|
49 | 'name': m.group(1), | |||
|
50 | 'text': m.group(2) | |||
|
51 | }) | |||
|
52 | ||||
|
53 | @undoc | |||
|
54 | class MathInlineGrammar(mistune.InlineGrammar): | |||
|
55 | math = re.compile("^\$(.+?)\$") | |||
|
56 | text = re.compile(r'^[\s\S]+?(?=[\\<!\[_*`~$]|https?://| {2,}\n|$)') | |||
|
57 | ||||
|
58 | @undoc | |||
|
59 | class MathInlineLexer(mistune.InlineLexer): | |||
|
60 | default_rules = ['math'] + mistune.InlineLexer.default_rules | |||
|
61 | ||||
|
62 | def __init__(self, renderer, rules=None, **kwargs): | |||
|
63 | if rules is None: | |||
|
64 | rules = MathInlineGrammar() | |||
|
65 | super(MathInlineLexer, self).__init__(renderer, rules, **kwargs) | |||
|
66 | ||||
|
67 | def output_math(self, m): | |||
|
68 | return self.renderer.inline_math(m.group(1)) | |||
|
69 | ||||
|
70 | @undoc | |||
|
71 | class MarkdownWithMath(mistune.Markdown): | |||
|
72 | def __init__(self, renderer, **kwargs): | |||
|
73 | if 'inline' not in kwargs: | |||
|
74 | kwargs['inline'] = MathInlineLexer | |||
|
75 | if 'block' not in kwargs: | |||
|
76 | kwargs['block'] = MathBlockLexer | |||
|
77 | super(MarkdownWithMath, self).__init__(renderer, **kwargs) | |||
|
78 | ||||
|
79 | def output_block_math(self): | |||
|
80 | return self.renderer.block_math(self.token['text']) | |||
|
81 | ||||
|
82 | def output_latex_environment(self): | |||
|
83 | return self.renderer.latex_environment(self.token['name'], self.token['text']) | |||
|
84 | ||||
|
85 | @undoc | |||
|
86 | class IPythonRenderer(mistune.Renderer): | |||
|
87 | def block_code(self, code, lang): | |||
|
88 | if lang: | |||
|
89 | try: | |||
|
90 | lexer = get_lexer_by_name(lang, stripall=True) | |||
|
91 | except ClassNotFound: | |||
|
92 | code = lang + '\n' + code | |||
|
93 | lang = None | |||
|
94 | ||||
|
95 | if not lang: | |||
|
96 | return '\n<pre><code>%s</code></pre>\n' % \ | |||
|
97 | mistune.escape(code) | |||
|
98 | ||||
|
99 | formatter = HtmlFormatter() | |||
|
100 | return highlight(code, lexer, formatter) | |||
|
101 | ||||
|
102 | def header(self, text, level, raw=None): | |||
|
103 | html = super(IPythonRenderer, self).header(text, level, raw=raw) | |||
|
104 | return add_anchor(html) | |||
|
105 | ||||
|
106 | # Pass math through unaltered - mathjax does the rendering in the browser | |||
|
107 | def block_math(self, text): | |||
|
108 | return '$$%s$$' % text | |||
|
109 | ||||
|
110 | def latex_environment(self, name, text): | |||
|
111 | return r'\begin{%s}%s\end{%s}' % (name, text, name) | |||
|
112 | ||||
|
113 | def inline_math(self, text): | |||
|
114 | return '$%s$' % text | |||
|
115 | ||||
|
116 | def markdown2html_mistune(source): | |||
|
117 | """Convert a markdown string to HTML using mistune""" | |||
|
118 | return MarkdownWithMath(renderer=IPythonRenderer()).render(source) |
@@ -34,7 +34,7 b' from IPython.core import page' | |||||
34 | from IPython.core import prefilter |
|
34 | from IPython.core import prefilter | |
35 | from IPython.core import shadowns |
|
35 | from IPython.core import shadowns | |
36 | from IPython.core import ultratb |
|
36 | from IPython.core import ultratb | |
37 |
from IPython.core.alias import Alias |
|
37 | from IPython.core.alias import Alias, AliasManager | |
38 | from IPython.core.autocall import ExitAutocall |
|
38 | from IPython.core.autocall import ExitAutocall | |
39 | from IPython.core.builtin_trap import BuiltinTrap |
|
39 | from IPython.core.builtin_trap import BuiltinTrap | |
40 | from IPython.core.events import EventManager, available_events |
|
40 | from IPython.core.events import EventManager, available_events | |
@@ -1502,6 +1502,7 b' class InteractiveShell(SingletonConfigurable):' | |||||
1502 | found = True |
|
1502 | found = True | |
1503 | ospace = 'IPython internal' |
|
1503 | ospace = 'IPython internal' | |
1504 | ismagic = True |
|
1504 | ismagic = True | |
|
1505 | isalias = isinstance(obj, Alias) | |||
1505 |
|
1506 | |||
1506 | # Last try: special-case some literals like '', [], {}, etc: |
|
1507 | # Last try: special-case some literals like '', [], {}, etc: | |
1507 | if not found and oname_head in ["''",'""','[]','{}','()']: |
|
1508 | if not found and oname_head in ["''",'""','[]','{}','()']: |
@@ -36,6 +36,7 b' from IPython.utils import io' | |||||
36 | from IPython.utils import openpy |
|
36 | from IPython.utils import openpy | |
37 | from IPython.utils import py3compat |
|
37 | from IPython.utils import py3compat | |
38 | from IPython.utils.dir2 import safe_hasattr |
|
38 | from IPython.utils.dir2 import safe_hasattr | |
|
39 | from IPython.utils.path import compress_user | |||
39 | from IPython.utils.text import indent |
|
40 | from IPython.utils.text import indent | |
40 | from IPython.utils.wildcard import list_namespace |
|
41 | from IPython.utils.wildcard import list_namespace | |
41 | from IPython.utils.coloransi import TermColors, ColorScheme, ColorSchemeTable |
|
42 | from IPython.utils.coloransi import TermColors, ColorScheme, ColorSchemeTable | |
@@ -554,23 +555,6 b' class Inspector:' | |||||
554 | title = header((title+":").ljust(title_width)) |
|
555 | title = header((title+":").ljust(title_width)) | |
555 | out.append(cast_unicode(title) + cast_unicode(content)) |
|
556 | out.append(cast_unicode(title) + cast_unicode(content)) | |
556 | return "\n".join(out) |
|
557 | return "\n".join(out) | |
557 |
|
||||
558 | # The fields to be displayed by pinfo: (fancy_name, key_in_info_dict) |
|
|||
559 | pinfo_fields1 = [("Type", "type_name"), |
|
|||
560 | ] |
|
|||
561 |
|
||||
562 | pinfo_fields2 = [("String form", "string_form"), |
|
|||
563 | ] |
|
|||
564 |
|
||||
565 | pinfo_fields3 = [("Length", "length"), |
|
|||
566 | ("File", "file"), |
|
|||
567 | ("Definition", "definition"), |
|
|||
568 | ] |
|
|||
569 |
|
||||
570 | pinfo_fields_obj = [("Class docstring", "class_docstring"), |
|
|||
571 | ("Init docstring", "init_docstring"), |
|
|||
572 | ("Call def", "call_def"), |
|
|||
573 | ("Call docstring", "call_docstring")] |
|
|||
574 |
|
558 | |||
575 | def _format_info(self, obj, oname='', formatter=None, info=None, detail_level=0): |
|
559 | def _format_info(self, obj, oname='', formatter=None, info=None, detail_level=0): | |
576 | """Format an info dict as text""" |
|
560 | """Format an info dict as text""" | |
@@ -582,41 +566,62 b' class Inspector:' | |||||
582 | field = info[key] |
|
566 | field = info[key] | |
583 | if field is not None: |
|
567 | if field is not None: | |
584 | displayfields.append((title, field.rstrip())) |
|
568 | displayfields.append((title, field.rstrip())) | |
585 |
|
569 | |||
586 | add_fields(self.pinfo_fields1) |
|
570 | if info['isalias']: | |
587 |
|
571 | add_fields([('Repr', "string_form")]) | ||
588 | # Base class for old-style instances |
|
572 | ||
589 | if (not py3compat.PY3) and isinstance(obj, types.InstanceType) and info['base_class']: |
|
573 | elif info['ismagic']: | |
590 | displayfields.append(("Base Class", info['base_class'].rstrip())) |
|
574 | add_fields([("Docstring", "docstring"), | |
591 |
|
575 | ("File", "file") | ||
592 | add_fields(self.pinfo_fields2) |
|
576 | ]) | |
593 |
|
577 | |||
594 | # Namespace |
|
578 | elif info['isclass'] or is_simple_callable(obj): | |
595 | if info['namespace'] != 'Interactive': |
|
579 | # Functions, methods, classes | |
596 | displayfields.append(("Namespace", info['namespace'].rstrip())) |
|
580 | add_fields([("Signature", "definition"), | |
597 |
|
581 | ("Init signature", "init_definition"), | ||
598 | add_fields(self.pinfo_fields3) |
|
582 | ]) | |
599 | if info['isclass'] and info['init_definition']: |
|
583 | if detail_level > 0 and info['source'] is not None: | |
600 | displayfields.append(("Init definition", |
|
584 | add_fields([("Source", "source")]) | |
601 | info['init_definition'].rstrip())) |
|
585 | else: | |
602 |
|
586 | add_fields([("Docstring", "docstring"), | ||
603 | # Source or docstring, depending on detail level and whether |
|
587 | ("Init docstring", "init_docstring"), | |
604 | # source found. |
|
588 | ]) | |
605 | if detail_level > 0 and info['source'] is not None: |
|
589 | ||
606 | displayfields.append(("Source", |
|
590 | add_fields([('File', 'file'), | |
607 | self.format(cast_unicode(info['source'])))) |
|
591 | ('Type', 'type_name'), | |
608 | elif info['docstring'] is not None: |
|
592 | ]) | |
609 | displayfields.append(("Docstring", info["docstring"])) |
|
593 | ||
610 |
|
||||
611 | # Constructor info for classes |
|
|||
612 | if info['isclass']: |
|
|||
613 | if info['init_docstring'] is not None: |
|
|||
614 | displayfields.append(("Init docstring", |
|
|||
615 | info['init_docstring'])) |
|
|||
616 |
|
||||
617 | # Info for objects: |
|
|||
618 | else: |
|
594 | else: | |
619 | add_fields(self.pinfo_fields_obj) |
|
595 | # General Python objects | |
|
596 | add_fields([("Type", "type_name")]) | |||
|
597 | ||||
|
598 | # Base class for old-style instances | |||
|
599 | if (not py3compat.PY3) and isinstance(obj, types.InstanceType) and info['base_class']: | |||
|
600 | displayfields.append(("Base Class", info['base_class'].rstrip())) | |||
|
601 | ||||
|
602 | add_fields([("String form", "string_form")]) | |||
|
603 | ||||
|
604 | # Namespace | |||
|
605 | if info['namespace'] != 'Interactive': | |||
|
606 | displayfields.append(("Namespace", info['namespace'].rstrip())) | |||
|
607 | ||||
|
608 | add_fields([("Length", "length"), | |||
|
609 | ("File", "file"), | |||
|
610 | ("Signature", "definition"), | |||
|
611 | ]) | |||
|
612 | ||||
|
613 | # Source or docstring, depending on detail level and whether | |||
|
614 | # source found. | |||
|
615 | if detail_level > 0 and info['source'] is not None: | |||
|
616 | displayfields.append(("Source", | |||
|
617 | self.format(cast_unicode(info['source'])))) | |||
|
618 | elif info['docstring'] is not None: | |||
|
619 | displayfields.append(("Docstring", info["docstring"])) | |||
|
620 | ||||
|
621 | add_fields([("Class docstring", "class_docstring"), | |||
|
622 | ("Init docstring", "init_docstring"), | |||
|
623 | ("Call signature", "call_def"), | |||
|
624 | ("Call docstring", "call_docstring")]) | |||
620 |
|
625 | |||
621 | if displayfields: |
|
626 | if displayfields: | |
622 | return self._format_fields(displayfields) |
|
627 | return self._format_fields(displayfields) | |
@@ -737,7 +742,7 b' class Inspector:' | |||||
737 | binary_file = True |
|
742 | binary_file = True | |
738 | elif fname.endswith('<string>'): |
|
743 | elif fname.endswith('<string>'): | |
739 | fname = 'Dynamically generated function. No source code available.' |
|
744 | fname = 'Dynamically generated function. No source code available.' | |
740 | out['file'] = fname |
|
745 | out['file'] = compress_user(fname) | |
741 |
|
746 | |||
742 | # Original source code for a callable, class or property. |
|
747 | # Original source code for a callable, class or property. | |
743 | if detail_level: |
|
748 | if detail_level: |
@@ -2,23 +2,10 b'' | |||||
2 | """ |
|
2 | """ | |
3 | A mixin for :class:`~IPython.core.application.Application` classes that |
|
3 | A mixin for :class:`~IPython.core.application.Application` classes that | |
4 | launch InteractiveShell instances, load extensions, etc. |
|
4 | launch InteractiveShell instances, load extensions, etc. | |
5 |
|
||||
6 | Authors |
|
|||
7 | ------- |
|
|||
8 |
|
||||
9 | * Min Ragan-Kelley |
|
|||
10 | """ |
|
5 | """ | |
11 |
|
6 | |||
12 | #----------------------------------------------------------------------------- |
|
7 | # Copyright (c) IPython Development Team. | |
13 | # Copyright (C) 2008-2011 The IPython Development Team |
|
8 | # Distributed under the terms of the Modified BSD License. | |
14 | # |
|
|||
15 | # Distributed under the terms of the BSD License. The full license is in |
|
|||
16 | # the file COPYING, distributed as part of this software. |
|
|||
17 | #----------------------------------------------------------------------------- |
|
|||
18 |
|
||||
19 | #----------------------------------------------------------------------------- |
|
|||
20 | # Imports |
|
|||
21 | #----------------------------------------------------------------------------- |
|
|||
22 |
|
9 | |||
23 | from __future__ import absolute_import |
|
10 | from __future__ import absolute_import | |
24 | from __future__ import print_function |
|
11 | from __future__ import print_function | |
@@ -152,10 +139,6 b' class InteractiveShellApp(Configurable):' | |||||
152 | extra_extension = Unicode('', config=True, |
|
139 | extra_extension = Unicode('', config=True, | |
153 | help="dotted module name of an IPython extension to load." |
|
140 | help="dotted module name of an IPython extension to load." | |
154 | ) |
|
141 | ) | |
155 | def _extra_extension_changed(self, name, old, new): |
|
|||
156 | if new: |
|
|||
157 | # add to self.extensions |
|
|||
158 | self.extensions.append(new) |
|
|||
159 |
|
142 | |||
160 | # Extensions that are always loaded (not configurable) |
|
143 | # Extensions that are always loaded (not configurable) | |
161 | default_extensions = List(Unicode, [u'storemagic'], config=False) |
|
144 | default_extensions = List(Unicode, [u'storemagic'], config=False) | |
@@ -269,6 +252,8 b' class InteractiveShellApp(Configurable):' | |||||
269 | try: |
|
252 | try: | |
270 | self.log.debug("Loading IPython extensions...") |
|
253 | self.log.debug("Loading IPython extensions...") | |
271 | extensions = self.default_extensions + self.extensions |
|
254 | extensions = self.default_extensions + self.extensions | |
|
255 | if self.extra_extension: | |||
|
256 | extensions.append(self.extra_extension) | |||
272 | for ext in extensions: |
|
257 | for ext in extensions: | |
273 | try: |
|
258 | try: | |
274 | self.log.info("Loading IPython extension: %s" % ext) |
|
259 | self.log.info("Loading IPython extension: %s" % ext) |
@@ -28,6 +28,7 b' from IPython.core.magic import (Magics, magics_class, line_magic,' | |||||
28 | register_line_cell_magic) |
|
28 | register_line_cell_magic) | |
29 | from IPython.external.decorator import decorator |
|
29 | from IPython.external.decorator import decorator | |
30 | from IPython.testing.decorators import skipif |
|
30 | from IPython.testing.decorators import skipif | |
|
31 | from IPython.utils.path import compress_user | |||
31 | from IPython.utils import py3compat |
|
32 | from IPython.utils import py3compat | |
32 |
|
33 | |||
33 |
|
34 | |||
@@ -46,7 +47,7 b' ip = get_ipython()' | |||||
46 | # defined, if any code is inserted above, the following line will need to be |
|
47 | # defined, if any code is inserted above, the following line will need to be | |
47 | # updated. Do NOT insert any whitespace between the next line and the function |
|
48 | # updated. Do NOT insert any whitespace between the next line and the function | |
48 | # definition below. |
|
49 | # definition below. | |
49 |
THIS_LINE_NUMBER = |
|
50 | THIS_LINE_NUMBER = 50 # Put here the actual number of this line | |
50 | def test_find_source_lines(): |
|
51 | def test_find_source_lines(): | |
51 | nt.assert_equal(oinspect.find_source_lines(test_find_source_lines), |
|
52 | nt.assert_equal(oinspect.find_source_lines(test_find_source_lines), | |
52 | THIS_LINE_NUMBER+1) |
|
53 | THIS_LINE_NUMBER+1) | |
@@ -241,7 +242,7 b' def test_info():' | |||||
241 | fname = fname[:-1] |
|
242 | fname = fname[:-1] | |
242 | # case-insensitive comparison needed on some filesystems |
|
243 | # case-insensitive comparison needed on some filesystems | |
243 | # e.g. Windows: |
|
244 | # e.g. Windows: | |
244 | nt.assert_equal(i['file'].lower(), fname.lower()) |
|
245 | nt.assert_equal(i['file'].lower(), compress_user(fname.lower())) | |
245 | nt.assert_equal(i['definition'], None) |
|
246 | nt.assert_equal(i['definition'], None) | |
246 | nt.assert_equal(i['docstring'], Call.__doc__) |
|
247 | nt.assert_equal(i['docstring'], Call.__doc__) | |
247 | nt.assert_equal(i['source'], None) |
|
248 | nt.assert_equal(i['source'], None) |
@@ -117,6 +117,7 b' define([' | |||||
117 | // don't return a comm, so that further .then() functions |
|
117 | // don't return a comm, so that further .then() functions | |
118 | // get an undefined comm input |
|
118 | // get an undefined comm input | |
119 | }); |
|
119 | }); | |
|
120 | return this.comms[content.comm_id]; | |||
120 | }; |
|
121 | }; | |
121 |
|
122 | |||
122 | CommManager.prototype.comm_msg = function(msg) { |
|
123 | CommManager.prototype.comm_msg = function(msg) { | |
@@ -134,6 +135,7 b' define([' | |||||
134 | } |
|
135 | } | |
135 | return comm; |
|
136 | return comm; | |
136 | }); |
|
137 | }); | |
|
138 | return this.comms[content.comm_id]; | |||
137 | }; |
|
139 | }; | |
138 |
|
140 | |||
139 | //----------------------------------------------------------------------- |
|
141 | //----------------------------------------------------------------------- |
@@ -41,6 +41,7 b' define([' | |||||
41 | this.username = "username"; |
|
41 | this.username = "username"; | |
42 | this.session_id = utils.uuid(); |
|
42 | this.session_id = utils.uuid(); | |
43 | this._msg_callbacks = {}; |
|
43 | this._msg_callbacks = {}; | |
|
44 | this._msg_queue = Promise.resolve(); | |||
44 | this.info_reply = {}; // kernel_info_reply stored here after starting |
|
45 | this.info_reply = {}; // kernel_info_reply stored here after starting | |
45 |
|
46 | |||
46 | if (typeof(WebSocket) !== 'undefined') { |
|
47 | if (typeof(WebSocket) !== 'undefined') { | |
@@ -854,19 +855,23 b' define([' | |||||
854 | }; |
|
855 | }; | |
855 |
|
856 | |||
856 | Kernel.prototype._handle_ws_message = function (e) { |
|
857 | Kernel.prototype._handle_ws_message = function (e) { | |
857 | serialize.deserialize(e.data, $.proxy(this._finish_ws_message, this)); |
|
858 | var that = this; | |
|
859 | this._msg_queue = this._msg_queue.then(function() { | |||
|
860 | return serialize.deserialize(e.data); | |||
|
861 | }).then(function(msg) {return that._finish_ws_message(msg);}) | |||
|
862 | .catch(utils.reject("Couldn't process kernel message", true)); | |||
858 | }; |
|
863 | }; | |
859 |
|
864 | |||
860 | Kernel.prototype._finish_ws_message = function (msg) { |
|
865 | Kernel.prototype._finish_ws_message = function (msg) { | |
861 | switch (msg.channel) { |
|
866 | switch (msg.channel) { | |
862 | case 'shell': |
|
867 | case 'shell': | |
863 | this._handle_shell_reply(msg); |
|
868 | return this._handle_shell_reply(msg); | |
864 | break; |
|
869 | break; | |
865 | case 'iopub': |
|
870 | case 'iopub': | |
866 | this._handle_iopub_message(msg); |
|
871 | return this._handle_iopub_message(msg); | |
867 | break; |
|
872 | break; | |
868 | case 'stdin': |
|
873 | case 'stdin': | |
869 | this._handle_input_request(msg); |
|
874 | return this._handle_input_request(msg); | |
870 | break; |
|
875 | break; | |
871 | default: |
|
876 | default: | |
872 | console.error("unrecognized message channel", msg.channel, msg); |
|
877 | console.error("unrecognized message channel", msg.channel, msg); | |
@@ -875,10 +880,12 b' define([' | |||||
875 |
|
880 | |||
876 | Kernel.prototype._handle_shell_reply = function (reply) { |
|
881 | Kernel.prototype._handle_shell_reply = function (reply) { | |
877 | this.events.trigger('shell_reply.Kernel', {kernel: this, reply:reply}); |
|
882 | this.events.trigger('shell_reply.Kernel', {kernel: this, reply:reply}); | |
|
883 | var that = this; | |||
878 | var content = reply.content; |
|
884 | var content = reply.content; | |
879 | var metadata = reply.metadata; |
|
885 | var metadata = reply.metadata; | |
880 | var parent_id = reply.parent_header.msg_id; |
|
886 | var parent_id = reply.parent_header.msg_id; | |
881 | var callbacks = this.get_callbacks_for_msg(parent_id); |
|
887 | var callbacks = this.get_callbacks_for_msg(parent_id); | |
|
888 | var promise = Promise.resolve(); | |||
882 | if (!callbacks || !callbacks.shell) { |
|
889 | if (!callbacks || !callbacks.shell) { | |
883 | return; |
|
890 | return; | |
884 | } |
|
891 | } | |
@@ -888,17 +895,21 b' define([' | |||||
888 | this._finish_shell(parent_id); |
|
895 | this._finish_shell(parent_id); | |
889 |
|
896 | |||
890 | if (shell_callbacks.reply !== undefined) { |
|
897 | if (shell_callbacks.reply !== undefined) { | |
891 | shell_callbacks.reply(reply); |
|
898 | promise = promise.then(function() {return shell_callbacks.reply(reply)}); | |
892 | } |
|
899 | } | |
893 | if (content.payload && shell_callbacks.payload) { |
|
900 | if (content.payload && shell_callbacks.payload) { | |
894 | this._handle_payloads(content.payload, shell_callbacks.payload, reply); |
|
901 | promise = promise.then(function() { | |
|
902 | return that._handle_payloads(content.payload, shell_callbacks.payload, reply); | |||
|
903 | }); | |||
895 | } |
|
904 | } | |
|
905 | return promise; | |||
896 | }; |
|
906 | }; | |
897 |
|
907 | |||
898 | /** |
|
908 | /** | |
899 | * @function _handle_payloads |
|
909 | * @function _handle_payloads | |
900 | */ |
|
910 | */ | |
901 | Kernel.prototype._handle_payloads = function (payloads, payload_callbacks, msg) { |
|
911 | Kernel.prototype._handle_payloads = function (payloads, payload_callbacks, msg) { | |
|
912 | var promise = []; | |||
902 | var l = payloads.length; |
|
913 | var l = payloads.length; | |
903 | // Payloads are handled by triggering events because we don't want the Kernel |
|
914 | // Payloads are handled by triggering events because we don't want the Kernel | |
904 | // to depend on the Notebook or Pager classes. |
|
915 | // to depend on the Notebook or Pager classes. | |
@@ -906,9 +917,10 b' define([' | |||||
906 | var payload = payloads[i]; |
|
917 | var payload = payloads[i]; | |
907 | var callback = payload_callbacks[payload.source]; |
|
918 | var callback = payload_callbacks[payload.source]; | |
908 | if (callback) { |
|
919 | if (callback) { | |
909 | callback(payload, msg); |
|
920 | promise.push(callback(payload, msg)); | |
910 | } |
|
921 | } | |
911 | } |
|
922 | } | |
|
923 | return Promise.all(promise); | |||
912 | }; |
|
924 | }; | |
913 |
|
925 | |||
914 | /** |
|
926 | /** | |
@@ -1021,7 +1033,7 b' define([' | |||||
1021 | Kernel.prototype._handle_iopub_message = function (msg) { |
|
1033 | Kernel.prototype._handle_iopub_message = function (msg) { | |
1022 | var handler = this.get_iopub_handler(msg.header.msg_type); |
|
1034 | var handler = this.get_iopub_handler(msg.header.msg_type); | |
1023 | if (handler !== undefined) { |
|
1035 | if (handler !== undefined) { | |
1024 | handler(msg); |
|
1036 | return handler(msg); | |
1025 | } |
|
1037 | } | |
1026 | }; |
|
1038 | }; | |
1027 |
|
1039 |
@@ -30,7 +30,7 b' define([' | |||||
30 | return msg; |
|
30 | return msg; | |
31 | }; |
|
31 | }; | |
32 |
|
32 | |||
33 |
var _deserialize_binary = function(data |
|
33 | var _deserialize_binary = function(data) { | |
34 | /** |
|
34 | /** | |
35 | * deserialize the binary message format |
|
35 | * deserialize the binary message format | |
36 | * callback will be called with a message whose buffers attribute |
|
36 | * callback will be called with a message whose buffers attribute | |
@@ -39,28 +39,31 b' define([' | |||||
39 | if (data instanceof Blob) { |
|
39 | if (data instanceof Blob) { | |
40 | // data is Blob, have to deserialize from ArrayBuffer in reader callback |
|
40 | // data is Blob, have to deserialize from ArrayBuffer in reader callback | |
41 | var reader = new FileReader(); |
|
41 | var reader = new FileReader(); | |
42 | reader.onload = function () { |
|
42 | var promise = new Promise(function(resolve, reject) { | |
43 | var msg = _deserialize_array_buffer(this.result); |
|
43 | reader.onload = function () { | |
44 | callback(msg); |
|
44 | var msg = _deserialize_array_buffer(this.result); | |
45 | }; |
|
45 | resolve(msg); | |
|
46 | }; | |||
|
47 | }); | |||
46 | reader.readAsArrayBuffer(data); |
|
48 | reader.readAsArrayBuffer(data); | |
|
49 | return promise; | |||
47 | } else { |
|
50 | } else { | |
48 | // data is ArrayBuffer, can deserialize directly |
|
51 | // data is ArrayBuffer, can deserialize directly | |
49 | var msg = _deserialize_array_buffer(data); |
|
52 | var msg = _deserialize_array_buffer(data); | |
50 |
|
|
53 | return msg; | |
51 | } |
|
54 | } | |
52 | }; |
|
55 | }; | |
53 |
|
56 | |||
54 |
var deserialize = function (data |
|
57 | var deserialize = function (data) { | |
55 | /** |
|
58 | /** | |
56 |
* deserialize a message and |
|
59 | * deserialize a message and return a promise for the unpacked message | |
57 | */ |
|
60 | */ | |
58 | if (typeof data === "string") { |
|
61 | if (typeof data === "string") { | |
59 | // text JSON message |
|
62 | // text JSON message | |
60 |
|
|
63 | return Promise.resolve(JSON.parse(data)); | |
61 | } else { |
|
64 | } else { | |
62 | // binary message |
|
65 | // binary message | |
63 |
_deserialize_binary(data |
|
66 | return Promise.resolve(_deserialize_binary(data)); | |
64 | } |
|
67 | } | |
65 | }; |
|
68 | }; | |
66 |
|
69 | |||
@@ -117,4 +120,4 b' define([' | |||||
117 | serialize: serialize |
|
120 | serialize: serialize | |
118 | }; |
|
121 | }; | |
119 | return exports; |
|
122 | return exports; | |
120 | }); No newline at end of file |
|
123 | }); |
@@ -1575,8 +1575,6 b' h6:hover .anchor-link {' | |||||
1575 | } |
|
1575 | } | |
1576 | .widget-radio-box label { |
|
1576 | .widget-radio-box label { | |
1577 | margin-top: 0px; |
|
1577 | margin-top: 0px; | |
1578 | } |
|
|||
1579 | .widget-radio { |
|
|||
1580 | margin-left: 20px; |
|
1578 | margin-left: 20px; | |
1581 | } |
|
1579 | } | |
1582 | /*# sourceMappingURL=ipython.min.css.map */ No newline at end of file |
|
1580 | /*# sourceMappingURL=ipython.min.css.map */ |
@@ -10358,8 +10358,6 b' h6:hover .anchor-link {' | |||||
10358 | } |
|
10358 | } | |
10359 | .widget-radio-box label { |
|
10359 | .widget-radio-box label { | |
10360 | margin-top: 0px; |
|
10360 | margin-top: 0px; | |
10361 | } |
|
|||
10362 | .widget-radio { |
|
|||
10363 | margin-left: 20px; |
|
10361 | margin-left: 20px; | |
10364 | } |
|
10362 | } | |
10365 | /*! |
|
10363 | /*! |
@@ -59,10 +59,9 b' define([' | |||||
59 | this.$checkbox.prop('checked', this.model.get('value')); |
|
59 | this.$checkbox.prop('checked', this.model.get('value')); | |
60 |
|
60 | |||
61 | if (options === undefined || options.updated_view != this) { |
|
61 | if (options === undefined || options.updated_view != this) { | |
62 |
|
|
62 | this.$checkbox.prop("disabled", this.model.get("disabled")); | |
63 | this.$checkbox.prop('disabled', disabled); |
|
|||
64 |
|
63 | |||
65 |
var description = this.model.get( |
|
64 | var description = this.model.get("description"); | |
66 | if (description.trim().length === 0) { |
|
65 | if (description.trim().length === 0) { | |
67 | this.$label.hide(); |
|
66 | this.$label.hide(); | |
68 | } else { |
|
67 | } else { | |
@@ -113,7 +112,7 b' define([' | |||||
113 | /** |
|
112 | /** | |
114 | * Update the contents of this view |
|
113 | * Update the contents of this view | |
115 | * |
|
114 | * | |
116 |
* Called when the model is changed. |
|
115 | * Called when the model is changed. The model may have been | |
117 | * changed by another view or by a state update from the back-end. |
|
116 | * changed by another view or by a state update from the back-end. | |
118 | */ |
|
117 | */ | |
119 | if (this.model.get('value')) { |
|
118 | if (this.model.get('value')) { | |
@@ -123,16 +122,16 b' define([' | |||||
123 | } |
|
122 | } | |
124 |
|
123 | |||
125 | if (options === undefined || options.updated_view != this) { |
|
124 | if (options === undefined || options.updated_view != this) { | |
126 |
|
125 | this.$el.prop("disabled", this.model.get("disabled")); | ||
127 | var disabled = this.model.get('disabled'); |
|
|||
128 | this.$el.prop('disabled', disabled); |
|
|||
129 |
|
||||
130 | var description = this.model.get('description'); |
|
|||
131 | this.$el.attr("title", this.model.get("tooltip")); |
|
126 | this.$el.attr("title", this.model.get("tooltip")); | |
132 | if (description.trim().length === 0) { |
|
127 | ||
|
128 | var description = this.model.get("description"); | |||
|
129 | var icon = this.model.get("icon"); | |||
|
130 | if (description.trim().length === 0 && icon.trim().length ===0) { | |||
133 | this.$el.html(" "); // Preserve button height |
|
131 | this.$el.html(" "); // Preserve button height | |
134 | } else { |
|
132 | } else { | |
135 | this.$el.text(description); |
|
133 | this.$el.text(description); | |
|
134 | $('<i class="fa"></i>').prependTo(this.$el).addClass(icon); | |||
136 | } |
|
135 | } | |
137 | } |
|
136 | } | |
138 | return ToggleButtonView.__super__.update.apply(this); |
|
137 | return ToggleButtonView.__super__.update.apply(this); |
@@ -27,21 +27,19 b' define([' | |||||
27 | /** |
|
27 | /** | |
28 | * Update the contents of this view |
|
28 | * Update the contents of this view | |
29 | * |
|
29 | * | |
30 |
* Called when the model is changed. |
|
30 | * Called when the model is changed. The model may have been | |
31 | * changed by another view or by a state update from the back-end. |
|
31 | * changed by another view or by a state update from the back-end. | |
32 | */ |
|
32 | */ | |
33 | var description = this.model.get('description'); |
|
33 | this.$el.prop("disabled", this.model.get("disabled")); | |
34 | this.$el.attr("title", this.model.get("tooltip")); |
|
34 | this.$el.attr("title", this.model.get("tooltip")); | |
35 | if (description.length === 0) { |
|
35 | ||
|
36 | var description = this.model.get("description"); | |||
|
37 | var icon = this.model.get("icon"); | |||
|
38 | if (description.trim().length === 0 && icon.trim().length ===0) { | |||
36 | this.$el.html(" "); // Preserve button height |
|
39 | this.$el.html(" "); // Preserve button height | |
37 | } else { |
|
40 | } else { | |
38 | this.$el.text(description); |
|
41 | this.$el.text(description); | |
39 | } |
|
42 | $('<i class="fa"></i>').prependTo(this.$el).addClass(icon); | |
40 |
|
||||
41 | if (this.model.get('disabled')) { |
|
|||
42 | this.$el.attr('disabled','disabled'); |
|
|||
43 | } else { |
|
|||
44 | this.$el.removeAttr('disabled'); |
|
|||
45 | } |
|
43 | } | |
46 |
|
44 | |||
47 | return ButtonView.__super__.update.apply(this); |
|
45 | return ButtonView.__super__.update.apply(this); |
@@ -307,17 +307,21 b' define([' | |||||
307 | if (options === undefined || options.updated_view != this) { |
|
307 | if (options === undefined || options.updated_view != this) { | |
308 | // Add missing items to the DOM. |
|
308 | // Add missing items to the DOM. | |
309 | var items = this.model.get('_options_labels'); |
|
309 | var items = this.model.get('_options_labels'); | |
|
310 | var icons = this.model.get('icons'); | |||
|
311 | var previous_icons = this.model.previous('icons') || []; | |||
310 | var disabled = this.model.get('disabled'); |
|
312 | var disabled = this.model.get('disabled'); | |
311 | var that = this; |
|
313 | var that = this; | |
312 | var item_html; |
|
314 | var item_html; | |
313 | _.each(items, function(item, index) { |
|
315 | _.each(items, function(item, index) { | |
314 |
if (item.trim().length === 0 |
|
316 | if (item.trim().length === 0 && (!icons[index] || | |
|
317 | icons[index].trim().length === 0)) { | |||
315 | item_html = " "; |
|
318 | item_html = " "; | |
316 | } else { |
|
319 | } else { | |
317 | item_html = utils.escape_html(item); |
|
320 | item_html = utils.escape_html(item); | |
318 | } |
|
321 | } | |
319 | var item_query = '[data-value="' + encodeURIComponent(item) + '"]'; |
|
322 | var item_query = '[data-value="' + encodeURIComponent(item) + '"]'; | |
320 | var $item_element = that.$buttongroup.find(item_query); |
|
323 | var $item_element = that.$buttongroup.find(item_query); | |
|
324 | var $icon_element = $item_element.find('.fa'); | |||
321 | if (!$item_element.length) { |
|
325 | if (!$item_element.length) { | |
322 | $item_element = $('<button/>') |
|
326 | $item_element = $('<button/>') | |
323 | .attr('type', 'button') |
|
327 | .attr('type', 'button') | |
@@ -325,16 +329,22 b' define([' | |||||
325 | .html(item_html) |
|
329 | .html(item_html) | |
326 | .appendTo(that.$buttongroup) |
|
330 | .appendTo(that.$buttongroup) | |
327 | .attr('data-value', encodeURIComponent(item)) |
|
331 | .attr('data-value', encodeURIComponent(item)) | |
|
332 | .attr('data-toggle', 'tooltip') | |||
328 | .attr('value', item) |
|
333 | .attr('value', item) | |
329 | .on('click', $.proxy(that.handle_click, that)); |
|
334 | .on('click', $.proxy(that.handle_click, that)); | |
330 | that.update_style_traits($item_element); |
|
335 | that.update_style_traits($item_element); | |
|
336 | $icon_element = $('<i class="fa"></i>').prependTo($item_element); | |||
331 | } |
|
337 | } | |
332 | if (that.model.get('selected_label') == item) { |
|
338 | if (that.model.get('selected_label') == item) { | |
333 | $item_element.addClass('active'); |
|
339 | $item_element.addClass('active'); | |
334 | } else { |
|
340 | } else { | |
335 | $item_element.removeClass('active'); |
|
341 | $item_element.removeClass('active'); | |
336 | } |
|
342 | } | |
337 |
$item_element.prop('disabled', disabled); |
|
343 | $item_element.prop('disabled', disabled); | |
|
344 | $item_element.attr('title', that.model.get('tooltips')[index]); | |||
|
345 | $icon_element | |||
|
346 | .removeClass(previous_icons[index]) | |||
|
347 | .addClass(icons[index]); | |||
338 | }); |
|
348 | }); | |
339 |
|
349 | |||
340 | // Remove items that no longer exist. |
|
350 | // Remove items that no longer exist. |
@@ -271,9 +271,6 b'' | |||||
271 |
|
271 | |||
272 | label { |
|
272 | label { | |
273 | margin-top: 0px; |
|
273 | margin-top: 0px; | |
|
274 | margin-left: 20px; | |||
274 | } |
|
275 | } | |
275 | } |
|
276 | } | |
276 |
|
||||
277 | .widget-radio { |
|
|||
278 | margin-left: 20px; |
|
|||
279 | } |
|
@@ -108,7 +108,7 b' data-notebook-path="{{notebook_path}}"' | |||||
108 | <li id="download_html"><a href="#">HTML (.html)</a></li> |
|
108 | <li id="download_html"><a href="#">HTML (.html)</a></li> | |
109 | <li id="download_markdown"><a href="#">Markdown (.md)</a></li> |
|
109 | <li id="download_markdown"><a href="#">Markdown (.md)</a></li> | |
110 | <li id="download_rst"><a href="#">reST (.rst)</a></li> |
|
110 | <li id="download_rst"><a href="#">reST (.rst)</a></li> | |
111 | <li id="download_pdf"><a href="#">PDF (.pdf)</a></li> |
|
111 | <li id="download_pdf"><a href="#">PDF via LaTeX (.pdf)</a></li> | |
112 | </ul> |
|
112 | </ul> | |
113 | </li> |
|
113 | </li> | |
114 | <li class="divider"></li> |
|
114 | <li class="divider"></li> |
@@ -51,7 +51,9 b' casper.notebook_test(function () {' | |||||
51 | this.then(function () { |
|
51 | this.then(function () { | |
52 | var index = this.append_cell([ |
|
52 | var index = this.append_cell([ | |
53 | "buffers = [b'\\xFF\\x00', b'\\x00\\x01\\x02']", |
|
53 | "buffers = [b'\\xFF\\x00', b'\\x00\\x01\\x02']", | |
54 |
"comm.send(data=' |
|
54 | "comm.send(data='message 0', buffers=buffers)", | |
|
55 | "comm.send(data='message 1')", | |||
|
56 | "comm.send(data='message 2', buffers=buffers)", | |||
55 | ].join('\n'), 'code'); |
|
57 | ].join('\n'), 'code'); | |
56 | this.execute_cell(index); |
|
58 | this.execute_cell(index); | |
57 | }); |
|
59 | }); | |
@@ -59,7 +61,7 b' casper.notebook_test(function () {' | |||||
59 | // wait for capture |
|
61 | // wait for capture | |
60 | this.waitFor(function () { |
|
62 | this.waitFor(function () { | |
61 | return this.evaluate(function () { |
|
63 | return this.evaluate(function () { | |
62 |
return IPython._msgs.length > |
|
64 | return IPython._msgs.length >= 3; | |
63 | }); |
|
65 | }); | |
64 | }); |
|
66 | }); | |
65 |
|
67 | |||
@@ -68,14 +70,22 b' casper.notebook_test(function () {' | |||||
68 | var msgs = this.evaluate(function () { |
|
70 | var msgs = this.evaluate(function () { | |
69 | return IPython._msgs; |
|
71 | return IPython._msgs; | |
70 | }); |
|
72 | }); | |
71 |
this.test.assertEquals(msgs.length, |
|
73 | this.test.assertEquals(msgs.length, 3, "Captured three comm messages"); | |
72 | var buffers = msgs[0].buffers; |
|
74 | ||
73 | this.test.assertEquals(buffers.length, 2, "comm message has buffers"); |
|
75 | ||
|
76 | ||||
|
77 | // check the messages came in the right order | |||
|
78 | this.test.assertEquals(msgs[0].content.data, "message 0", "message 0 processed first"); | |||
|
79 | this.test.assertEquals(msgs[0].buffers.length, 2, "comm message 0 has two buffers"); | |||
|
80 | this.test.assertEquals(msgs[1].content.data, "message 1", "message 1 processed second"); | |||
|
81 | this.test.assertEquals(msgs[1].buffers.length, 0, "comm message 1 has no buffers"); | |||
|
82 | this.test.assertEquals(msgs[2].content.data, "message 2", "message 2 processed third"); | |||
|
83 | this.test.assertEquals(msgs[2].buffers.length, 2, "comm message 2 has two buffers"); | |||
74 |
|
84 | |||
75 | // extract attributes to test in evaluate, |
|
85 | // extract attributes to test in evaluate, | |
76 | // because the raw DataViews can't be passed across |
|
86 | // because the raw DataViews can't be passed across | |
77 | var buf_info = function (index) { |
|
87 | var buf_info = function (message, index) { | |
78 |
var buf = IPython._msgs[ |
|
88 | var buf = IPython._msgs[message].buffers[index]; | |
79 | var data = {}; |
|
89 | var data = {}; | |
80 | data.byteLength = buf.byteLength; |
|
90 | data.byteLength = buf.byteLength; | |
81 | data.bytes = []; |
|
91 | data.bytes = []; | |
@@ -85,18 +95,22 b' casper.notebook_test(function () {' | |||||
85 | return data; |
|
95 | return data; | |
86 | }; |
|
96 | }; | |
87 |
|
97 | |||
88 | buf0 = this.evaluate(buf_info, 0); |
|
98 | var msgs_with_buffers = [0, 2]; | |
89 | buf1 = this.evaluate(buf_info, 1); |
|
99 | for (var i = 0; i < msgs_with_buffers.length; i++) { | |
90 | this.test.assertEquals(buf0.byteLength, 2, 'buf[0] has correct size'); |
|
100 | msg_index = msgs_with_buffers[i]; | |
91 | this.test.assertEquals(buf0.bytes, [255, 0], 'buf[0] has correct bytes'); |
|
101 | buf0 = this.evaluate(buf_info, msg_index, 0); | |
92 | this.test.assertEquals(buf1.byteLength, 3, 'buf[1] has correct size'); |
|
102 | buf1 = this.evaluate(buf_info, msg_index, 1); | |
93 |
this.test.assertEquals(buf |
|
103 | this.test.assertEquals(buf0.byteLength, 2, 'buf[0] has correct size in message '+msg_index); | |
|
104 | this.test.assertEquals(buf0.bytes, [255, 0], 'buf[0] has correct bytes in message '+msg_index); | |||
|
105 | this.test.assertEquals(buf1.byteLength, 3, 'buf[1] has correct size in message '+msg_index); | |||
|
106 | this.test.assertEquals(buf1.bytes, [0, 1, 2], 'buf[1] has correct bytes in message '+msg_index); | |||
|
107 | } | |||
94 | }); |
|
108 | }); | |
95 |
|
109 | |||
96 | // validate captured buffers Python-side |
|
110 | // validate captured buffers Python-side | |
97 | this.then(function () { |
|
111 | this.then(function () { | |
98 | var index = this.append_cell([ |
|
112 | var index = this.append_cell([ | |
99 |
"assert len(msgs) == |
|
113 | "assert len(msgs) == 3, len(msgs)", | |
100 | "bufs = msgs[0]['buffers']", |
|
114 | "bufs = msgs[0]['buffers']", | |
101 | "assert len(bufs) == len(buffers), bufs", |
|
115 | "assert len(bufs) == len(buffers), bufs", | |
102 | "assert bufs[0].bytes == buffers[0], bufs[0].bytes", |
|
116 | "assert bufs[0].bytes == buffers[0], bufs[0].bytes", | |
@@ -107,7 +121,7 b' casper.notebook_test(function () {' | |||||
107 | this.wait_for_output(index); |
|
121 | this.wait_for_output(index); | |
108 | this.then(function () { |
|
122 | this.then(function () { | |
109 | var out = this.get_output_cell(index); |
|
123 | var out = this.get_output_cell(index); | |
110 | this.test.assertEquals(out['text/plain'], '1', "Python received buffers"); |
|
124 | this.test.assertEquals(out.data['text/plain'], '1', "Python received buffers"); | |
111 | }); |
|
125 | }); | |
112 | }); |
|
126 | }); | |
113 | }); |
|
127 | }); |
@@ -49,7 +49,7 b' casper.notebook_test(function () {' | |||||
49 | 'Toggle button exists.'); |
|
49 | 'Toggle button exists.'); | |
50 |
|
50 | |||
51 | this.test.assert(this.cell_element_function(bool_index, |
|
51 | this.test.assert(this.cell_element_function(bool_index, | |
52 |
widget_togglebutton_selector, 'html')== |
|
52 | widget_togglebutton_selector, 'html')=='<i class="fa"></i>Title', | |
53 | 'Toggle button labeled correctly.'); |
|
53 | 'Toggle button labeled correctly.'); | |
54 |
|
54 | |||
55 | this.test.assert(this.cell_element_function(bool_index, |
|
55 | this.test.assert(this.cell_element_function(bool_index, |
@@ -29,7 +29,7 b' casper.notebook_test(function () {' | |||||
29 | 'Widget button exists.'); |
|
29 | 'Widget button exists.'); | |
30 |
|
30 | |||
31 | this.test.assert(this.cell_element_function(button_index, |
|
31 | this.test.assert(this.cell_element_function(button_index, | |
32 | widget_button_selector, 'html')=='Title', |
|
32 | widget_button_selector, 'html')=='<i class="fa"></i>Title', | |
33 | 'Set button description.'); |
|
33 | 'Set button description.'); | |
34 |
|
34 | |||
35 | this.cell_element_function(button_index, |
|
35 | this.cell_element_function(button_index, |
@@ -55,10 +55,15 b' class ToggleButton(_Bool):' | |||||
55 | value of the toggle button: True-pressed, False-unpressed |
|
55 | value of the toggle button: True-pressed, False-unpressed | |
56 | description : str |
|
56 | description : str | |
57 | description displayed next to the button |
|
57 | description displayed next to the button | |
|
58 | tooltip: str | |||
|
59 | tooltip caption of the toggle button | |||
|
60 | icon: str | |||
|
61 | font-awesome icon name | |||
58 | """ |
|
62 | """ | |
59 |
|
63 | |||
60 | _view_name = Unicode('ToggleButtonView', sync=True) |
|
64 | _view_name = Unicode('ToggleButtonView', sync=True) | |
61 | tooltip = Unicode(help="Tooltip caption of the toggle button.", sync=True) |
|
65 | tooltip = Unicode(help="Tooltip caption of the toggle button.", sync=True) | |
|
66 | icon = Unicode('', help= "Font-awesome icon.", sync=True) | |||
62 |
|
67 | |||
63 | button_style = CaselessStrEnum( |
|
68 | button_style = CaselessStrEnum( | |
64 | values=['primary', 'success', 'info', 'warning', 'danger', ''], |
|
69 | values=['primary', 'success', 'info', 'warning', 'danger', ''], |
@@ -24,15 +24,25 b' from IPython.utils.warn import DeprecatedClass' | |||||
24 | @register('IPython.Button') |
|
24 | @register('IPython.Button') | |
25 | class Button(DOMWidget): |
|
25 | class Button(DOMWidget): | |
26 | """Button widget. |
|
26 | """Button widget. | |
|
27 | This widget has an `on_click` method that allows you to listen for the | |||
|
28 | user clicking on the button. The click event itself is stateless. | |||
27 |
|
29 | |||
28 | This widget has an `on_click` method that allows you to listen for the |
|
30 | Parameters | |
29 | user clicking on the button. The click event itself is stateless.""" |
|
31 | ---------- | |
|
32 | description : str | |||
|
33 | description displayed next to the button | |||
|
34 | tooltip: str | |||
|
35 | tooltip caption of the toggle button | |||
|
36 | icon: str | |||
|
37 | font-awesome icon name | |||
|
38 | """ | |||
30 | _view_name = Unicode('ButtonView', sync=True) |
|
39 | _view_name = Unicode('ButtonView', sync=True) | |
31 |
|
40 | |||
32 | # Keys |
|
41 | # Keys | |
33 | description = Unicode('', help="Button label.", sync=True) |
|
42 | description = Unicode('', help="Button label.", sync=True) | |
34 | tooltip = Unicode(help="Tooltip caption of the button.", sync=True) |
|
43 | tooltip = Unicode(help="Tooltip caption of the button.", sync=True) | |
35 | disabled = Bool(False, help="Enable or disable user changes.", sync=True) |
|
44 | disabled = Bool(False, help="Enable or disable user changes.", sync=True) | |
|
45 | icon = Unicode('', help= "Font-awesome icon.", sync=True) | |||
36 |
|
46 | |||
37 | button_style = CaselessStrEnum( |
|
47 | button_style = CaselessStrEnum( | |
38 | values=['primary', 'success', 'info', 'warning', 'danger', ''], |
|
48 | values=['primary', 'success', 'info', 'warning', 'danger', ''], |
@@ -19,7 +19,7 b' from threading import Lock' | |||||
19 |
|
19 | |||
20 | from .widget import DOMWidget, register |
|
20 | from .widget import DOMWidget, register | |
21 | from IPython.utils.traitlets import ( |
|
21 | from IPython.utils.traitlets import ( | |
22 | Unicode, Bool, Any, Dict, TraitError, CaselessStrEnum, Tuple |
|
22 | Unicode, Bool, Any, Dict, TraitError, CaselessStrEnum, Tuple, List | |
23 | ) |
|
23 | ) | |
24 | from IPython.utils.py3compat import unicode_type |
|
24 | from IPython.utils.py3compat import unicode_type | |
25 | from IPython.utils.warn import DeprecatedClass |
|
25 | from IPython.utils.warn import DeprecatedClass | |
@@ -32,6 +32,12 b' class _Selection(DOMWidget):' | |||||
32 |
|
32 | |||
33 | ``options`` can be specified as a list or dict. If given as a list, |
|
33 | ``options`` can be specified as a list or dict. If given as a list, | |
34 | it will be transformed to a dict of the form ``{str(value):value}``. |
|
34 | it will be transformed to a dict of the form ``{str(value):value}``. | |
|
35 | ||||
|
36 | When programmatically setting the value, a reverse lookup is performed | |||
|
37 | among the options to set the value of ``selected_label`` accordingly. The | |||
|
38 | reverse lookup uses the equality operator by default, but an other | |||
|
39 | predicate may be provided via the ``equals`` argument. For example, when | |||
|
40 | dealing with numpy arrays, one may set equals=np.array_equal. | |||
35 | """ |
|
41 | """ | |
36 |
|
42 | |||
37 | value = Any(help="Selected value") |
|
43 | value = Any(help="Selected value") | |
@@ -194,6 +200,8 b' class ToggleButtons(_Selection):' | |||||
194 | """Group of toggle buttons that represent an enumeration. Only one toggle |
|
200 | """Group of toggle buttons that represent an enumeration. Only one toggle | |
195 | button can be toggled at any point in time.""" |
|
201 | button can be toggled at any point in time.""" | |
196 | _view_name = Unicode('ToggleButtonsView', sync=True) |
|
202 | _view_name = Unicode('ToggleButtonsView', sync=True) | |
|
203 | tooltips = List(Unicode(), sync=True) | |||
|
204 | icons = List(Unicode(), sync=True) | |||
197 |
|
205 | |||
198 | button_style = CaselessStrEnum( |
|
206 | button_style = CaselessStrEnum( | |
199 | values=['primary', 'success', 'info', 'warning', 'danger', ''], |
|
207 | values=['primary', 'success', 'info', 'warning', 'danger', ''], |
@@ -384,7 +384,7 b' def test_oinfo_detail():' | |||||
384 | content = reply['content'] |
|
384 | content = reply['content'] | |
385 | assert content['found'] |
|
385 | assert content['found'] | |
386 | text = content['data']['text/plain'] |
|
386 | text = content['data']['text/plain'] | |
387 |
nt.assert_in(' |
|
387 | nt.assert_in('Signature:', text) | |
388 | nt.assert_in('Source:', text) |
|
388 | nt.assert_in('Source:', text) | |
389 |
|
389 | |||
390 |
|
390 |
@@ -91,7 +91,7 b' class TestSession(SessionTestCase):' | |||||
91 |
|
91 | |||
92 | content = msg['content'] |
|
92 | content = msg['content'] | |
93 | header = msg['header'] |
|
93 | header = msg['header'] | |
94 | header['date'] = datetime.now() |
|
94 | header['msg_id'] = self.session.msg_id | |
95 | parent = msg['parent_header'] |
|
95 | parent = msg['parent_header'] | |
96 | metadata = msg['metadata'] |
|
96 | metadata = msg['metadata'] | |
97 | msg_type = header['msg_type'] |
|
97 | msg_type = header['msg_type'] | |
@@ -100,7 +100,7 b' class TestSession(SessionTestCase):' | |||||
100 | ident, msg_list = self.session.feed_identities(B.recv_multipart()) |
|
100 | ident, msg_list = self.session.feed_identities(B.recv_multipart()) | |
101 | new_msg = self.session.deserialize(msg_list) |
|
101 | new_msg = self.session.deserialize(msg_list) | |
102 | self.assertEqual(ident[0], b'foo') |
|
102 | self.assertEqual(ident[0], b'foo') | |
103 |
self.assertEqual(new_msg['msg_id'], |
|
103 | self.assertEqual(new_msg['msg_id'],header['msg_id']) | |
104 | self.assertEqual(new_msg['msg_type'],msg['msg_type']) |
|
104 | self.assertEqual(new_msg['msg_type'],msg['msg_type']) | |
105 | self.assertEqual(new_msg['header'],msg['header']) |
|
105 | self.assertEqual(new_msg['header'],msg['header']) | |
106 | self.assertEqual(new_msg['content'],msg['content']) |
|
106 | self.assertEqual(new_msg['content'],msg['content']) | |
@@ -108,12 +108,12 b' class TestSession(SessionTestCase):' | |||||
108 | self.assertEqual(new_msg['parent_header'],msg['parent_header']) |
|
108 | self.assertEqual(new_msg['parent_header'],msg['parent_header']) | |
109 | self.assertEqual(new_msg['buffers'],[b'bar']) |
|
109 | self.assertEqual(new_msg['buffers'],[b'bar']) | |
110 |
|
110 | |||
111 | header['date'] = datetime.now() |
|
111 | header['msg_id'] = self.session.msg_id | |
112 |
|
112 | |||
113 | self.session.send(A, msg, ident=b'foo', buffers=[b'bar']) |
|
113 | self.session.send(A, msg, ident=b'foo', buffers=[b'bar']) | |
114 | ident, new_msg = self.session.recv(B) |
|
114 | ident, new_msg = self.session.recv(B) | |
115 | self.assertEqual(ident[0], b'foo') |
|
115 | self.assertEqual(ident[0], b'foo') | |
116 |
self.assertEqual(new_msg['msg_id'], |
|
116 | self.assertEqual(new_msg['msg_id'],header['msg_id']) | |
117 | self.assertEqual(new_msg['msg_type'],msg['msg_type']) |
|
117 | self.assertEqual(new_msg['msg_type'],msg['msg_type']) | |
118 | self.assertEqual(new_msg['header'],msg['header']) |
|
118 | self.assertEqual(new_msg['header'],msg['header']) | |
119 | self.assertEqual(new_msg['content'],msg['content']) |
|
119 | self.assertEqual(new_msg['content'],msg['content']) |
@@ -142,9 +142,10 b' class Exporter(LoggingConfigurable):' | |||||
142 | resources = ResourcesDict() |
|
142 | resources = ResourcesDict() | |
143 | if not 'metadata' in resources or resources['metadata'] == '': |
|
143 | if not 'metadata' in resources or resources['metadata'] == '': | |
144 | resources['metadata'] = ResourcesDict() |
|
144 | resources['metadata'] = ResourcesDict() | |
145 |
basename = os.path. |
|
145 | path, basename = os.path.split(filename) | |
146 | notebook_name = basename[:basename.rfind('.')] |
|
146 | notebook_name = basename[:basename.rfind('.')] | |
147 | resources['metadata']['name'] = notebook_name |
|
147 | resources['metadata']['name'] = notebook_name | |
|
148 | resources['metadata']['path'] = path | |||
148 |
|
149 | |||
149 | modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename)) |
|
150 | modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename)) | |
150 | resources['metadata']['modified_date'] = modified_date.strftime(text.date_format) |
|
151 | resources['metadata']['modified_date'] = modified_date.strftime(text.date_format) |
@@ -8,23 +8,21 b' markdown within Jinja templates.' | |||||
8 |
|
8 | |||
9 | from __future__ import print_function |
|
9 | from __future__ import print_function | |
10 |
|
10 | |||
11 | # Stdlib imports |
|
|||
12 | import os |
|
11 | import os | |
13 | import subprocess |
|
12 | import subprocess | |
14 | from io import TextIOWrapper, BytesIO |
|
13 | from io import TextIOWrapper, BytesIO | |
15 | import re |
|
|||
16 |
|
14 | |||
17 | import mistune |
|
15 | try: | |
18 | from pygments import highlight |
|
16 | from .markdown_mistune import markdown2html_mistune | |
19 | from pygments.lexers import get_lexer_by_name |
|
17 | except ImportError as e: | |
20 | from pygments.formatters import HtmlFormatter |
|
18 | # store in variable for Python 3 | |
21 | from pygments.util import ClassNotFound |
|
19 | _mistune_import_error = e | |
|
20 | def markdown2html_mistune(source): | |||
|
21 | """mistune is unavailable, raise ImportError""" | |||
|
22 | raise ImportError("markdown2html requires mistune: %s" % _mistune_import_error) | |||
22 |
|
23 | |||
23 | # IPython imports |
|
|||
24 | from IPython.nbconvert.filters.strings import add_anchor |
|
|||
25 | from IPython.nbconvert.utils.pandoc import pandoc |
|
24 | from IPython.nbconvert.utils.pandoc import pandoc | |
26 | from IPython.nbconvert.utils.exceptions import ConversionException |
|
25 | from IPython.nbconvert.utils.exceptions import ConversionException | |
27 | from IPython.utils.decorators import undoc |
|
|||
28 | from IPython.utils.process import get_output_error_code |
|
26 | from IPython.utils.process import get_output_error_code | |
29 | from IPython.utils.py3compat import cast_bytes |
|
27 | from IPython.utils.py3compat import cast_bytes | |
30 | from IPython.utils.version import check_version |
|
28 | from IPython.utils.version import check_version | |
@@ -46,6 +44,7 b' class NodeJSMissing(ConversionException):' | |||||
46 | """Exception raised when node.js is missing.""" |
|
44 | """Exception raised when node.js is missing.""" | |
47 | pass |
|
45 | pass | |
48 |
|
46 | |||
|
47 | ||||
49 | def markdown2latex(source, markup='markdown', extra_args=None): |
|
48 | def markdown2latex(source, markup='markdown', extra_args=None): | |
50 | """Convert a markdown string to LaTeX via pandoc. |
|
49 | """Convert a markdown string to LaTeX via pandoc. | |
51 |
|
50 | |||
@@ -69,107 +68,12 b" def markdown2latex(source, markup='markdown', extra_args=None):" | |||||
69 | return pandoc(source, markup, 'latex', extra_args=extra_args) |
|
68 | return pandoc(source, markup, 'latex', extra_args=extra_args) | |
70 |
|
69 | |||
71 |
|
70 | |||
72 | @undoc |
|
|||
73 | class MathBlockGrammar(mistune.BlockGrammar): |
|
|||
74 | block_math = re.compile("^\$\$(.*?)\$\$", re.DOTALL) |
|
|||
75 | latex_environment = re.compile(r"^\\begin\{([a-z]*\*?)\}(.*?)\\end\{\1\}", |
|
|||
76 | re.DOTALL) |
|
|||
77 |
|
||||
78 | @undoc |
|
|||
79 | class MathBlockLexer(mistune.BlockLexer): |
|
|||
80 | default_rules = ['block_math', 'latex_environment'] + mistune.BlockLexer.default_rules |
|
|||
81 |
|
||||
82 | def __init__(self, rules=None, **kwargs): |
|
|||
83 | if rules is None: |
|
|||
84 | rules = MathBlockGrammar() |
|
|||
85 | super(MathBlockLexer, self).__init__(rules, **kwargs) |
|
|||
86 |
|
||||
87 | def parse_block_math(self, m): |
|
|||
88 | """Parse a $$math$$ block""" |
|
|||
89 | self.tokens.append({ |
|
|||
90 | 'type': 'block_math', |
|
|||
91 | 'text': m.group(1) |
|
|||
92 | }) |
|
|||
93 |
|
||||
94 | def parse_latex_environment(self, m): |
|
|||
95 | self.tokens.append({ |
|
|||
96 | 'type': 'latex_environment', |
|
|||
97 | 'name': m.group(1), |
|
|||
98 | 'text': m.group(2) |
|
|||
99 | }) |
|
|||
100 |
|
||||
101 | @undoc |
|
|||
102 | class MathInlineGrammar(mistune.InlineGrammar): |
|
|||
103 | math = re.compile("^\$(.+?)\$") |
|
|||
104 | text = re.compile(r'^[\s\S]+?(?=[\\<!\[_*`~$]|https?://| {2,}\n|$)') |
|
|||
105 |
|
||||
106 | @undoc |
|
|||
107 | class MathInlineLexer(mistune.InlineLexer): |
|
|||
108 | default_rules = ['math'] + mistune.InlineLexer.default_rules |
|
|||
109 |
|
||||
110 | def __init__(self, renderer, rules=None, **kwargs): |
|
|||
111 | if rules is None: |
|
|||
112 | rules = MathInlineGrammar() |
|
|||
113 | super(MathInlineLexer, self).__init__(renderer, rules, **kwargs) |
|
|||
114 |
|
||||
115 | def output_math(self, m): |
|
|||
116 | return self.renderer.inline_math(m.group(1)) |
|
|||
117 |
|
||||
118 | @undoc |
|
|||
119 | class MarkdownWithMath(mistune.Markdown): |
|
|||
120 | def __init__(self, renderer, **kwargs): |
|
|||
121 | if 'inline' not in kwargs: |
|
|||
122 | kwargs['inline'] = MathInlineLexer |
|
|||
123 | if 'block' not in kwargs: |
|
|||
124 | kwargs['block'] = MathBlockLexer |
|
|||
125 | super(MarkdownWithMath, self).__init__(renderer, **kwargs) |
|
|||
126 |
|
||||
127 | def output_block_math(self): |
|
|||
128 | return self.renderer.block_math(self.token['text']) |
|
|||
129 |
|
||||
130 | def output_latex_environment(self): |
|
|||
131 | return self.renderer.latex_environment(self.token['name'], self.token['text']) |
|
|||
132 |
|
||||
133 | @undoc |
|
|||
134 | class IPythonRenderer(mistune.Renderer): |
|
|||
135 | def block_code(self, code, lang): |
|
|||
136 | if lang: |
|
|||
137 | try: |
|
|||
138 | lexer = get_lexer_by_name(lang, stripall=True) |
|
|||
139 | except ClassNotFound: |
|
|||
140 | code = lang + '\n' + code |
|
|||
141 | lang = None |
|
|||
142 |
|
||||
143 | if not lang: |
|
|||
144 | return '\n<pre><code>%s</code></pre>\n' % \ |
|
|||
145 | mistune.escape(code) |
|
|||
146 |
|
||||
147 | formatter = HtmlFormatter() |
|
|||
148 | return highlight(code, lexer, formatter) |
|
|||
149 |
|
||||
150 | def header(self, text, level, raw=None): |
|
|||
151 | html = super(IPythonRenderer, self).header(text, level, raw=raw) |
|
|||
152 | return add_anchor(html) |
|
|||
153 |
|
||||
154 | # Pass math through unaltered - mathjax does the rendering in the browser |
|
|||
155 | def block_math(self, text): |
|
|||
156 | return '$$%s$$' % text |
|
|||
157 |
|
||||
158 | def latex_environment(self, name, text): |
|
|||
159 | return r'\begin{%s}%s\end{%s}' % (name, text, name) |
|
|||
160 |
|
||||
161 | def inline_math(self, text): |
|
|||
162 | return '$%s$' % text |
|
|||
163 |
|
||||
164 | def markdown2html_mistune(source): |
|
|||
165 | """Convert a markdown string to HTML using mistune""" |
|
|||
166 | return MarkdownWithMath(renderer=IPythonRenderer()).render(source) |
|
|||
167 |
|
||||
168 | def markdown2html_pandoc(source, extra_args=None): |
|
71 | def markdown2html_pandoc(source, extra_args=None): | |
169 | """Convert a markdown string to HTML via pandoc""" |
|
72 | """Convert a markdown string to HTML via pandoc""" | |
170 | extra_args = extra_args or ['--mathjax'] |
|
73 | extra_args = extra_args or ['--mathjax'] | |
171 | return pandoc(source, 'markdown', 'html', extra_args=extra_args) |
|
74 | return pandoc(source, 'markdown', 'html', extra_args=extra_args) | |
172 |
|
75 | |||
|
76 | ||||
173 | def _find_nodejs(): |
|
77 | def _find_nodejs(): | |
174 | global _node |
|
78 | global _node | |
175 | if _node is None: |
|
79 | if _node is None: |
@@ -25,6 +25,14 b' class FilesWriter(WriterBase):' | |||||
25 | help="""Directory to write output to. Leave blank |
|
25 | help="""Directory to write output to. Leave blank | |
26 | to output to the current directory""") |
|
26 | to output to the current directory""") | |
27 |
|
27 | |||
|
28 | relpath = Unicode( | |||
|
29 | "", config=True, | |||
|
30 | help="""When copying files that the notebook depends on, copy them in | |||
|
31 | relation to this path, such that the destination filename will be | |||
|
32 | os.path.relpath(filename, relpath). If FilesWriter is operating on a | |||
|
33 | notebook that already exists elsewhere on disk, then the default will be | |||
|
34 | the directory containing that notebook.""") | |||
|
35 | ||||
28 |
|
36 | |||
29 | # Make sure that the output directory exists. |
|
37 | # Make sure that the output directory exists. | |
30 | def _build_directory_changed(self, name, old, new): |
|
38 | def _build_directory_changed(self, name, old, new): | |
@@ -59,6 +67,12 b' class FilesWriter(WriterBase):' | |||||
59 | # Pull the extension and subdir from the resources dict. |
|
67 | # Pull the extension and subdir from the resources dict. | |
60 | output_extension = resources.get('output_extension', None) |
|
68 | output_extension = resources.get('output_extension', None) | |
61 |
|
69 | |||
|
70 | # Get the relative path for copying files | |||
|
71 | if self.relpath == '': | |||
|
72 | relpath = resources.get('metadata', {}).get('path', '') | |||
|
73 | else: | |||
|
74 | relpath = self.relpath | |||
|
75 | ||||
62 | # Write all of the extracted resources to the destination directory. |
|
76 | # Write all of the extracted resources to the destination directory. | |
63 | # NOTE: WE WRITE EVERYTHING AS-IF IT'S BINARY. THE EXTRACT FIG |
|
77 | # NOTE: WE WRITE EVERYTHING AS-IF IT'S BINARY. THE EXTRACT FIG | |
64 | # PREPROCESSOR SHOULD HANDLE UNIX/WINDOWS LINE ENDINGS... |
|
78 | # PREPROCESSOR SHOULD HANDLE UNIX/WINDOWS LINE ENDINGS... | |
@@ -81,8 +95,14 b' class FilesWriter(WriterBase):' | |||||
81 | # Copy files that match search pattern |
|
95 | # Copy files that match search pattern | |
82 | for matching_filename in glob.glob(filename): |
|
96 | for matching_filename in glob.glob(filename): | |
83 |
|
97 | |||
|
98 | # compute the relative path for the filename | |||
|
99 | if relpath != '': | |||
|
100 | dest_filename = os.path.relpath(matching_filename, relpath) | |||
|
101 | else: | |||
|
102 | dest_filename = matching_filename | |||
|
103 | ||||
84 | # Make sure folder exists. |
|
104 | # Make sure folder exists. | |
85 |
dest = os.path.join(self.build_directory, |
|
105 | dest = os.path.join(self.build_directory, dest_filename) | |
86 | path = os.path.dirname(dest) |
|
106 | path = os.path.dirname(dest) | |
87 | self._makedir(path) |
|
107 | self._makedir(path) | |
88 |
|
108 |
@@ -201,3 +201,107 b' class Testfiles(TestsBase):' | |||||
201 | with open(dest, 'r') as f: |
|
201 | with open(dest, 'r') as f: | |
202 | output = f.read() |
|
202 | output = f.read() | |
203 | self.assertEqual(output, 'e') |
|
203 | self.assertEqual(output, 'e') | |
|
204 | ||||
|
205 | def test_relpath(self): | |||
|
206 | """Can the FilesWriter handle relative paths for linked files correctly?""" | |||
|
207 | ||||
|
208 | # Work in a temporary directory. | |||
|
209 | with self.create_temp_cwd(): | |||
|
210 | ||||
|
211 | # Create test file | |||
|
212 | os.mkdir('sub') | |||
|
213 | with open(os.path.join('sub', 'c'), 'w') as f: | |||
|
214 | f.write('d') | |||
|
215 | ||||
|
216 | # Create the resoruces dictionary | |||
|
217 | res = {} | |||
|
218 | ||||
|
219 | # Create files writer, test output | |||
|
220 | writer = FilesWriter() | |||
|
221 | writer.files = [os.path.join('sub', 'c')] | |||
|
222 | writer.build_directory = u'build' | |||
|
223 | writer.relpath = 'sub' | |||
|
224 | writer.write(u'y', res, notebook_name="z") | |||
|
225 | ||||
|
226 | # Check the output of the file | |||
|
227 | assert os.path.isdir(writer.build_directory) | |||
|
228 | dest = os.path.join(writer.build_directory, 'z') | |||
|
229 | with open(dest, 'r') as f: | |||
|
230 | output = f.read() | |||
|
231 | self.assertEqual(output, u'y') | |||
|
232 | ||||
|
233 | # Check to make sure the linked file was copied | |||
|
234 | dest = os.path.join(writer.build_directory, 'c') | |||
|
235 | assert os.path.isfile(dest) | |||
|
236 | with open(dest, 'r') as f: | |||
|
237 | output = f.read() | |||
|
238 | self.assertEqual(output, 'd') | |||
|
239 | ||||
|
240 | def test_relpath_default(self): | |||
|
241 | """Is the FilesWriter default relative path correct?""" | |||
|
242 | ||||
|
243 | # Work in a temporary directory. | |||
|
244 | with self.create_temp_cwd(): | |||
|
245 | ||||
|
246 | # Create test file | |||
|
247 | os.mkdir('sub') | |||
|
248 | with open(os.path.join('sub', 'c'), 'w') as f: | |||
|
249 | f.write('d') | |||
|
250 | ||||
|
251 | # Create the resoruces dictionary | |||
|
252 | res = dict(metadata=dict(path="sub")) | |||
|
253 | ||||
|
254 | # Create files writer, test output | |||
|
255 | writer = FilesWriter() | |||
|
256 | writer.files = [os.path.join('sub', 'c')] | |||
|
257 | writer.build_directory = u'build' | |||
|
258 | writer.write(u'y', res, notebook_name="z") | |||
|
259 | ||||
|
260 | # Check the output of the file | |||
|
261 | assert os.path.isdir(writer.build_directory) | |||
|
262 | dest = os.path.join(writer.build_directory, 'z') | |||
|
263 | with open(dest, 'r') as f: | |||
|
264 | output = f.read() | |||
|
265 | self.assertEqual(output, u'y') | |||
|
266 | ||||
|
267 | # Check to make sure the linked file was copied | |||
|
268 | dest = os.path.join(writer.build_directory, 'c') | |||
|
269 | assert os.path.isfile(dest) | |||
|
270 | with open(dest, 'r') as f: | |||
|
271 | output = f.read() | |||
|
272 | self.assertEqual(output, 'd') | |||
|
273 | ||||
|
274 | def test_relpath_default(self): | |||
|
275 | """Does the FilesWriter relpath option take precedence over the path?""" | |||
|
276 | ||||
|
277 | # Work in a temporary directory. | |||
|
278 | with self.create_temp_cwd(): | |||
|
279 | ||||
|
280 | # Create test file | |||
|
281 | os.mkdir('sub') | |||
|
282 | with open(os.path.join('sub', 'c'), 'w') as f: | |||
|
283 | f.write('d') | |||
|
284 | ||||
|
285 | # Create the resoruces dictionary | |||
|
286 | res = dict(metadata=dict(path="other_sub")) | |||
|
287 | ||||
|
288 | # Create files writer, test output | |||
|
289 | writer = FilesWriter() | |||
|
290 | writer.files = [os.path.join('sub', 'c')] | |||
|
291 | writer.build_directory = u'build' | |||
|
292 | writer.relpath = 'sub' | |||
|
293 | writer.write(u'y', res, notebook_name="z") | |||
|
294 | ||||
|
295 | # Check the output of the file | |||
|
296 | assert os.path.isdir(writer.build_directory) | |||
|
297 | dest = os.path.join(writer.build_directory, 'z') | |||
|
298 | with open(dest, 'r') as f: | |||
|
299 | output = f.read() | |||
|
300 | self.assertEqual(output, u'y') | |||
|
301 | ||||
|
302 | # Check to make sure the linked file was copied | |||
|
303 | dest = os.path.join(writer.build_directory, 'c') | |||
|
304 | assert os.path.isfile(dest) | |||
|
305 | with open(dest, 'r') as f: | |||
|
306 | output = f.read() | |||
|
307 | self.assertEqual(output, 'd') |
@@ -48,7 +48,7 b'' | |||||
48 | .. _scipy: http://www.scipy.org |
|
48 | .. _scipy: http://www.scipy.org | |
49 | .. _scipy_conference: http://conference.scipy.org |
|
49 | .. _scipy_conference: http://conference.scipy.org | |
50 | .. _matplotlib: http://matplotlib.org |
|
50 | .. _matplotlib: http://matplotlib.org | |
51 |
.. _pythonxy: http |
|
51 | .. _pythonxy: https://code.google.com/p/pythonxy/ | |
52 | .. _ETS: http://code.enthought.com/projects/tool-suite.php |
|
52 | .. _ETS: http://code.enthought.com/projects/tool-suite.php | |
53 | .. _EPD: http://www.enthought.com/products/epd.php |
|
53 | .. _EPD: http://www.enthought.com/products/epd.php | |
54 | .. _python: http://www.python.org |
|
54 | .. _python: http://www.python.org |
@@ -307,7 +307,7 b'' | |||||
307 | "range1, range2, range3 = widgets.IntSlider(description='Range 1'),\\\n", |
|
307 | "range1, range2, range3 = widgets.IntSlider(description='Range 1'),\\\n", | |
308 | " widgets.IntSlider(description='Range 2'),\\\n", |
|
308 | " widgets.IntSlider(description='Range 2'),\\\n", | |
309 | " widgets.IntSlider(description='Range 3')\n", |
|
309 | " widgets.IntSlider(description='Range 3')\n", | |
310 | "l = widgets.link((range1, 'value'), (range2, 'value'), (range3, 'value'))\n", |
|
310 | "l = widgets.jslink((range1, 'value'), (range2, 'value'), (range3, 'value'))\n", | |
311 | "display(caption, range1, range2, range3)" |
|
311 | "display(caption, range1, range2, range3)" | |
312 | ] |
|
312 | ] | |
313 | }, |
|
313 | }, | |
@@ -323,7 +323,7 b'' | |||||
323 | "source_range, target_range1, target_range2 = widgets.IntSlider(description='Source range'),\\\n", |
|
323 | "source_range, target_range1, target_range2 = widgets.IntSlider(description='Source range'),\\\n", | |
324 | " widgets.IntSlider(description='Target range 1'),\\\n", |
|
324 | " widgets.IntSlider(description='Target range 1'),\\\n", | |
325 | " widgets.IntSlider(description='Target range 2')\n", |
|
325 | " widgets.IntSlider(description='Target range 2')\n", | |
326 | "widgets.dlink((source_range, 'value'), (target_range1, 'value'), (target_range2, 'value'))\n", |
|
326 | "widgets.jsdlink((source_range, 'value'), (target_range1, 'value'), (target_range2, 'value'))\n", | |
327 | "display(caption, source_range, target_range1, target_range2)" |
|
327 | "display(caption, source_range, target_range1, target_range2)" | |
328 | ] |
|
328 | ] | |
329 | }, |
|
329 | }, |
General Comments 0
You need to be logged in to leave comments.
Login now