Show More
@@ -0,0 +1,63 | |||||
|
1 | """Tests for tokenutil""" | |||
|
2 | # Copyright (c) IPython Development Team. | |||
|
3 | # Distributed under the terms of the Modified BSD License. | |||
|
4 | ||||
|
5 | import nose.tools as nt | |||
|
6 | ||||
|
7 | from IPython.utils.tokenutil import token_at_cursor | |||
|
8 | ||||
|
9 | def expect_token(expected, cell, cursor_pos): | |||
|
10 | token = token_at_cursor(cell, cursor_pos) | |||
|
11 | offset = 0 | |||
|
12 | for line in cell.splitlines(): | |||
|
13 | if offset + len(line) >= cursor_pos: | |||
|
14 | break | |||
|
15 | else: | |||
|
16 | offset += len(line) | |||
|
17 | column = cursor_pos - offset | |||
|
18 | line_with_cursor = '%s|%s' % (line[:column], line[column:]) | |||
|
19 | line | |||
|
20 | nt.assert_equal(token, expected, | |||
|
21 | "Excpected %r, got %r in: %s" % ( | |||
|
22 | expected, token, line_with_cursor) | |||
|
23 | ) | |||
|
24 | ||||
|
25 | def test_simple(): | |||
|
26 | cell = "foo" | |||
|
27 | for i in range(len(cell)): | |||
|
28 | expect_token("foo", cell, i) | |||
|
29 | ||||
|
30 | def test_function(): | |||
|
31 | cell = "foo(a=5, b='10')" | |||
|
32 | expected = 'foo' | |||
|
33 | for i in (6,7,8,10,11,12): | |||
|
34 | expect_token("foo", cell, i) | |||
|
35 | ||||
|
36 | def test_multiline(): | |||
|
37 | cell = '\n'.join([ | |||
|
38 | 'a = 5', | |||
|
39 | 'b = hello("string", there)' | |||
|
40 | ]) | |||
|
41 | expected = 'hello' | |||
|
42 | start = cell.index(expected) | |||
|
43 | for i in range(start, start + len(expected)): | |||
|
44 | expect_token(expected, cell, i) | |||
|
45 | expected = 'there' | |||
|
46 | start = cell.index(expected) | |||
|
47 | for i in range(start, start + len(expected)): | |||
|
48 | expect_token(expected, cell, i) | |||
|
49 | ||||
|
50 | def test_attrs(): | |||
|
51 | cell = "foo(a=obj.attr.subattr)" | |||
|
52 | expected = 'obj' | |||
|
53 | idx = cell.find('obj') | |||
|
54 | for i in range(idx, idx + 3): | |||
|
55 | expect_token(expected, cell, i) | |||
|
56 | idx = idx + 4 | |||
|
57 | expected = 'obj.attr' | |||
|
58 | for i in range(idx, idx + 4): | |||
|
59 | expect_token(expected, cell, i) | |||
|
60 | idx = idx + 5 | |||
|
61 | expected = 'obj.attr.subattr' | |||
|
62 | for i in range(idx, len(cell)): | |||
|
63 | expect_token(expected, cell, i) |
@@ -0,0 +1,78 | |||||
|
1 | """Token-related utilities""" | |||
|
2 | ||||
|
3 | # Copyright (c) IPython Development Team. | |||
|
4 | # Distributed under the terms of the Modified BSD License. | |||
|
5 | ||||
|
6 | from __future__ import absolute_import, print_function | |||
|
7 | ||||
|
8 | from collections import namedtuple | |||
|
9 | from io import StringIO | |||
|
10 | from keyword import iskeyword | |||
|
11 | ||||
|
12 | from . import tokenize2 | |||
|
13 | from .py3compat import cast_unicode_py2 | |||
|
14 | ||||
|
15 | Token = namedtuple('Token', ['token', 'text', 'start', 'end', 'line']) | |||
|
16 | ||||
|
17 | def generate_tokens(readline): | |||
|
18 | """wrap generate_tokens to catch EOF errors""" | |||
|
19 | try: | |||
|
20 | for token in tokenize2.generate_tokens(readline): | |||
|
21 | yield token | |||
|
22 | except tokenize2.TokenError: | |||
|
23 | # catch EOF error | |||
|
24 | return | |||
|
25 | ||||
|
26 | def token_at_cursor(cell, cursor_pos=0): | |||
|
27 | """Get the token at a given cursor | |||
|
28 | ||||
|
29 | Used for introspection. | |||
|
30 | ||||
|
31 | Parameters | |||
|
32 | ---------- | |||
|
33 | ||||
|
34 | cell : unicode | |||
|
35 | A block of Python code | |||
|
36 | cursor_pos : int | |||
|
37 | The location of the cursor in the block where the token should be found | |||
|
38 | """ | |||
|
39 | cell = cast_unicode_py2(cell) | |||
|
40 | names = [] | |||
|
41 | tokens = [] | |||
|
42 | offset = 0 | |||
|
43 | for tup in generate_tokens(StringIO(cell).readline): | |||
|
44 | ||||
|
45 | tok = Token(*tup) | |||
|
46 | ||||
|
47 | # token, text, start, end, line = tup | |||
|
48 | start_col = tok.start[1] | |||
|
49 | end_col = tok.end[1] | |||
|
50 | if offset + start_col > cursor_pos: | |||
|
51 | # current token starts after the cursor, | |||
|
52 | # don't consume it | |||
|
53 | break | |||
|
54 | ||||
|
55 | if tok.token == tokenize2.NAME and not iskeyword(tok.text): | |||
|
56 | if names and tokens and tokens[-1].token == tokenize2.OP and tokens[-1].text == '.': | |||
|
57 | names[-1] = "%s.%s" % (names[-1], tok.text) | |||
|
58 | else: | |||
|
59 | names.append(tok.text) | |||
|
60 | elif tok.token == tokenize2.OP: | |||
|
61 | if tok.text == '=' and names: | |||
|
62 | # don't inspect the lhs of an assignment | |||
|
63 | names.pop(-1) | |||
|
64 | ||||
|
65 | if offset + end_col > cursor_pos: | |||
|
66 | # we found the cursor, stop reading | |||
|
67 | break | |||
|
68 | ||||
|
69 | tokens.append(tok) | |||
|
70 | if tok.token == tokenize2.NEWLINE: | |||
|
71 | offset += len(tok.line) | |||
|
72 | ||||
|
73 | if names: | |||
|
74 | return names[-1] | |||
|
75 | else: | |||
|
76 | return '' | |||
|
77 | ||||
|
78 |
@@ -0,0 +1,64 | |||||
|
1 | .. _execution_semantics: | |||
|
2 | ||||
|
3 | Execution semantics in the IPython kernel | |||
|
4 | ========================================= | |||
|
5 | ||||
|
6 | The execution of use code consists of the following phases: | |||
|
7 | ||||
|
8 | 1. Fire the ``pre_execute`` event. | |||
|
9 | 2. Fire the ``pre_run_cell`` event unless silent is True. | |||
|
10 | 3. Execute the ``code`` field, see below for details. | |||
|
11 | 4. If execution succeeds, expressions in ``user_expressions`` are computed. | |||
|
12 | This ensures that any error in the expressions don't affect the main code execution. | |||
|
13 | 5. Fire the post_execute event. | |||
|
14 | ||||
|
15 | .. seealso:: | |||
|
16 | ||||
|
17 | :doc:`config/callbacks` | |||
|
18 | ||||
|
19 | ||||
|
20 | To understand how the ``code`` field is executed, one must know that Python | |||
|
21 | code can be compiled in one of three modes (controlled by the ``mode`` argument | |||
|
22 | to the :func:`compile` builtin): | |||
|
23 | ||||
|
24 | *single* | |||
|
25 | Valid for a single interactive statement (though the source can contain | |||
|
26 | multiple lines, such as a for loop). When compiled in this mode, the | |||
|
27 | generated bytecode contains special instructions that trigger the calling of | |||
|
28 | :func:`sys.displayhook` for any expression in the block that returns a value. | |||
|
29 | This means that a single statement can actually produce multiple calls to | |||
|
30 | :func:`sys.displayhook`, if for example it contains a loop where each | |||
|
31 | iteration computes an unassigned expression would generate 10 calls:: | |||
|
32 | ||||
|
33 | for i in range(10): | |||
|
34 | i**2 | |||
|
35 | ||||
|
36 | *exec* | |||
|
37 | An arbitrary amount of source code, this is how modules are compiled. | |||
|
38 | :func:`sys.displayhook` is *never* implicitly called. | |||
|
39 | ||||
|
40 | *eval* | |||
|
41 | A single expression that returns a value. :func:`sys.displayhook` is *never* | |||
|
42 | implicitly called. | |||
|
43 | ||||
|
44 | ||||
|
45 | The ``code`` field is split into individual blocks each of which is valid for | |||
|
46 | execution in 'single' mode, and then: | |||
|
47 | ||||
|
48 | - If there is only a single block: it is executed in 'single' mode. | |||
|
49 | ||||
|
50 | - If there is more than one block: | |||
|
51 | ||||
|
52 | * if the last one is a single line long, run all but the last in 'exec' mode | |||
|
53 | and the very last one in 'single' mode. This makes it easy to type simple | |||
|
54 | expressions at the end to see computed values. | |||
|
55 | ||||
|
56 | * if the last one is no more than two lines long, run all but the last in | |||
|
57 | 'exec' mode and the very last one in 'single' mode. This makes it easy to | |||
|
58 | type simple expressions at the end to see computed values. - otherwise | |||
|
59 | (last one is also multiline), run all in 'exec' mode | |||
|
60 | ||||
|
61 | * otherwise (last one is also multiline), run all in 'exec' mode as a single | |||
|
62 | unit. | |||
|
63 | ||||
|
64 |
@@ -126,13 +126,13 def display(*objs, **kwargs): | |||||
126 | else: |
|
126 | else: | |
127 | continue |
|
127 | continue | |
128 | if raw: |
|
128 | if raw: | |
129 |
publish_display_data( |
|
129 | publish_display_data(data=obj, metadata=metadata) | |
130 | else: |
|
130 | else: | |
131 | format_dict, md_dict = format(obj, include=include, exclude=exclude) |
|
131 | format_dict, md_dict = format(obj, include=include, exclude=exclude) | |
132 | if metadata: |
|
132 | if metadata: | |
133 | # kwarg-specified metadata gets precedence |
|
133 | # kwarg-specified metadata gets precedence | |
134 | _merge(md_dict, metadata) |
|
134 | _merge(md_dict, metadata) | |
135 |
publish_display_data( |
|
135 | publish_display_data(data=format_dict, metadata=md_dict) | |
136 |
|
136 | |||
137 |
|
137 | |||
138 | def display_pretty(*objs, **kwargs): |
|
138 | def display_pretty(*objs, **kwargs): |
@@ -10,22 +10,10 There are two components of the display system: | |||||
10 | This module defines the logic display publishing. The display publisher uses |
|
10 | This module defines the logic display publishing. The display publisher uses | |
11 | the ``display_data`` message type that is defined in the IPython messaging |
|
11 | the ``display_data`` message type that is defined in the IPython messaging | |
12 | spec. |
|
12 | spec. | |
13 |
|
||||
14 | Authors: |
|
|||
15 |
|
||||
16 | * Brian Granger |
|
|||
17 | """ |
|
13 | """ | |
18 |
|
14 | |||
19 | #----------------------------------------------------------------------------- |
|
15 | # Copyright (c) IPython Development Team. | |
20 | # Copyright (C) 2008-2011 The IPython Development Team |
|
16 | # Distributed under the terms of the Modified BSD License. | |
21 | # |
|
|||
22 | # Distributed under the terms of the BSD License. The full license is in |
|
|||
23 | # the file COPYING, distributed as part of this software. |
|
|||
24 | #----------------------------------------------------------------------------- |
|
|||
25 |
|
||||
26 | #----------------------------------------------------------------------------- |
|
|||
27 | # Imports |
|
|||
28 | #----------------------------------------------------------------------------- |
|
|||
29 |
|
17 | |||
30 | from __future__ import print_function |
|
18 | from __future__ import print_function | |
31 |
|
19 | |||
@@ -45,29 +33,24 class DisplayPublisher(Configurable): | |||||
45 | be accessed there. |
|
33 | be accessed there. | |
46 | """ |
|
34 | """ | |
47 |
|
35 | |||
48 |
def _validate_data(self |
|
36 | def _validate_data(self, data, metadata=None): | |
49 | """Validate the display data. |
|
37 | """Validate the display data. | |
50 |
|
38 | |||
51 | Parameters |
|
39 | Parameters | |
52 | ---------- |
|
40 | ---------- | |
53 | source : str |
|
|||
54 | The fully dotted name of the callable that created the data, like |
|
|||
55 | :func:`foo.bar.my_formatter`. |
|
|||
56 | data : dict |
|
41 | data : dict | |
57 | The formata data dictionary. |
|
42 | The formata data dictionary. | |
58 | metadata : dict |
|
43 | metadata : dict | |
59 | Any metadata for the data. |
|
44 | Any metadata for the data. | |
60 | """ |
|
45 | """ | |
61 |
|
46 | |||
62 | if not isinstance(source, string_types): |
|
|||
63 | raise TypeError('source must be a str, got: %r' % source) |
|
|||
64 | if not isinstance(data, dict): |
|
47 | if not isinstance(data, dict): | |
65 | raise TypeError('data must be a dict, got: %r' % data) |
|
48 | raise TypeError('data must be a dict, got: %r' % data) | |
66 | if metadata is not None: |
|
49 | if metadata is not None: | |
67 | if not isinstance(metadata, dict): |
|
50 | if not isinstance(metadata, dict): | |
68 | raise TypeError('metadata must be a dict, got: %r' % data) |
|
51 | raise TypeError('metadata must be a dict, got: %r' % data) | |
69 |
|
52 | |||
70 |
def publish(self, |
|
53 | def publish(self, data, metadata=None, source=None): | |
71 | """Publish data and metadata to all frontends. |
|
54 | """Publish data and metadata to all frontends. | |
72 |
|
55 | |||
73 | See the ``display_data`` message in the messaging documentation for |
|
56 | See the ``display_data`` message in the messaging documentation for | |
@@ -87,9 +70,6 class DisplayPublisher(Configurable): | |||||
87 |
|
70 | |||
88 | Parameters |
|
71 | Parameters | |
89 | ---------- |
|
72 | ---------- | |
90 | source : str |
|
|||
91 | A string that give the function or method that created the data, |
|
|||
92 | such as 'IPython.core.page'. |
|
|||
93 | data : dict |
|
73 | data : dict | |
94 | A dictionary having keys that are valid MIME types (like |
|
74 | A dictionary having keys that are valid MIME types (like | |
95 | 'text/plain' or 'image/svg+xml') and values that are the data for |
|
75 | 'text/plain' or 'image/svg+xml') and values that are the data for | |
@@ -104,6 +84,8 class DisplayPublisher(Configurable): | |||||
104 | the data. Metadata specific to each mime-type can be specified |
|
84 | the data. Metadata specific to each mime-type can be specified | |
105 | in the metadata dict with the same mime-type keys as |
|
85 | in the metadata dict with the same mime-type keys as | |
106 | the data itself. |
|
86 | the data itself. | |
|
87 | source : str, deprecated | |||
|
88 | Unused. | |||
107 | """ |
|
89 | """ | |
108 |
|
90 | |||
109 | # The default is to simply write the plain text data using io.stdout. |
|
91 | # The default is to simply write the plain text data using io.stdout. | |
@@ -122,8 +104,8 class CapturingDisplayPublisher(DisplayPublisher): | |||||
122 | """A DisplayPublisher that stores""" |
|
104 | """A DisplayPublisher that stores""" | |
123 | outputs = List() |
|
105 | outputs = List() | |
124 |
|
106 | |||
125 |
def publish(self, |
|
107 | def publish(self, data, metadata=None, source=None): | |
126 |
self.outputs.append(( |
|
108 | self.outputs.append((data, metadata)) | |
127 |
|
109 | |||
128 | def clear_output(self, wait=False): |
|
110 | def clear_output(self, wait=False): | |
129 | super(CapturingDisplayPublisher, self).clear_output(wait) |
|
111 | super(CapturingDisplayPublisher, self).clear_output(wait) | |
@@ -132,7 +114,7 class CapturingDisplayPublisher(DisplayPublisher): | |||||
132 | del self.outputs[:] |
|
114 | del self.outputs[:] | |
133 |
|
115 | |||
134 |
|
116 | |||
135 |
def publish_display_data( |
|
117 | def publish_display_data(data, metadata=None, source=None): | |
136 | """Publish data and metadata to all frontends. |
|
118 | """Publish data and metadata to all frontends. | |
137 |
|
119 | |||
138 | See the ``display_data`` message in the messaging documentation for |
|
120 | See the ``display_data`` message in the messaging documentation for | |
@@ -152,9 +134,6 def publish_display_data(source, data, metadata=None): | |||||
152 |
|
134 | |||
153 | Parameters |
|
135 | Parameters | |
154 | ---------- |
|
136 | ---------- | |
155 | source : str |
|
|||
156 | A string that give the function or method that created the data, |
|
|||
157 | such as 'IPython.core.page'. |
|
|||
158 | data : dict |
|
137 | data : dict | |
159 | A dictionary having keys that are valid MIME types (like |
|
138 | A dictionary having keys that are valid MIME types (like | |
160 | 'text/plain' or 'image/svg+xml') and values that are the data for |
|
139 | 'text/plain' or 'image/svg+xml') and values that are the data for | |
@@ -168,12 +147,13 def publish_display_data(source, data, metadata=None): | |||||
168 | arbitrary key, value pairs that frontends can use to interpret |
|
147 | arbitrary key, value pairs that frontends can use to interpret | |
169 | the data. mime-type keys matching those in data can be used |
|
148 | the data. mime-type keys matching those in data can be used | |
170 | to specify metadata about particular representations. |
|
149 | to specify metadata about particular representations. | |
|
150 | source : str, deprecated | |||
|
151 | Unused. | |||
171 | """ |
|
152 | """ | |
172 | from IPython.core.interactiveshell import InteractiveShell |
|
153 | from IPython.core.interactiveshell import InteractiveShell | |
173 | InteractiveShell.instance().display_pub.publish( |
|
154 | InteractiveShell.instance().display_pub.publish( | |
174 | source, |
|
155 | data=data, | |
175 | data, |
|
156 | metadata=metadata, | |
176 | metadata |
|
|||
177 | ) |
|
157 | ) | |
178 |
|
158 | |||
179 |
|
159 |
@@ -10,12 +10,7 | |||||
10 | # the file COPYING, distributed as part of this software. |
|
10 | # the file COPYING, distributed as part of this software. | |
11 | #----------------------------------------------------------------------------- |
|
11 | #----------------------------------------------------------------------------- | |
12 |
|
12 | |||
13 | #----------------------------------------------------------------------------- |
|
13 | from __future__ import absolute_import, print_function | |
14 | # Imports |
|
|||
15 | #----------------------------------------------------------------------------- |
|
|||
16 |
|
||||
17 | from __future__ import absolute_import |
|
|||
18 | from __future__ import print_function |
|
|||
19 |
|
14 | |||
20 | import __future__ |
|
15 | import __future__ | |
21 | import abc |
|
16 | import abc | |
@@ -57,6 +52,7 from IPython.core.payload import PayloadManager | |||||
57 | from IPython.core.prefilter import PrefilterManager |
|
52 | from IPython.core.prefilter import PrefilterManager | |
58 | from IPython.core.profiledir import ProfileDir |
|
53 | from IPython.core.profiledir import ProfileDir | |
59 | from IPython.core.prompts import PromptManager |
|
54 | from IPython.core.prompts import PromptManager | |
|
55 | from IPython.core.usage import default_banner | |||
60 | from IPython.lib.latextools import LaTeXTool |
|
56 | from IPython.lib.latextools import LaTeXTool | |
61 | from IPython.testing.skipdoctest import skip_doctest |
|
57 | from IPython.testing.skipdoctest import skip_doctest | |
62 | from IPython.utils import PyColorize |
|
58 | from IPython.utils import PyColorize | |
@@ -233,6 +229,16 class InteractiveShell(SingletonConfigurable): | |||||
233 | Enable magic commands to be called without the leading %. |
|
229 | Enable magic commands to be called without the leading %. | |
234 | """ |
|
230 | """ | |
235 | ) |
|
231 | ) | |
|
232 | ||||
|
233 | banner = Unicode('') | |||
|
234 | ||||
|
235 | banner1 = Unicode(default_banner, config=True, | |||
|
236 | help="""The part of the banner to be printed before the profile""" | |||
|
237 | ) | |||
|
238 | banner2 = Unicode('', config=True, | |||
|
239 | help="""The part of the banner to be printed after the profile""" | |||
|
240 | ) | |||
|
241 | ||||
236 | cache_size = Integer(1000, config=True, help= |
|
242 | cache_size = Integer(1000, config=True, help= | |
237 | """ |
|
243 | """ | |
238 | Set the size of the output cache. The default is 1000, you can |
|
244 | Set the size of the output cache. The default is 1000, you can | |
@@ -773,6 +779,24 class InteractiveShell(SingletonConfigurable): | |||||
773 | sys.modules[self._orig_sys_modules_main_name] = self._orig_sys_modules_main_mod |
|
779 | sys.modules[self._orig_sys_modules_main_name] = self._orig_sys_modules_main_mod | |
774 |
|
780 | |||
775 | #------------------------------------------------------------------------- |
|
781 | #------------------------------------------------------------------------- | |
|
782 | # Things related to the banner | |||
|
783 | #------------------------------------------------------------------------- | |||
|
784 | ||||
|
785 | @property | |||
|
786 | def banner(self): | |||
|
787 | banner = self.banner1 | |||
|
788 | if self.profile and self.profile != 'default': | |||
|
789 | banner += '\nIPython profile: %s\n' % self.profile | |||
|
790 | if self.banner2: | |||
|
791 | banner += '\n' + self.banner2 | |||
|
792 | return banner | |||
|
793 | ||||
|
794 | def show_banner(self, banner=None): | |||
|
795 | if banner is None: | |||
|
796 | banner = self.banner | |||
|
797 | self.write(banner) | |||
|
798 | ||||
|
799 | #------------------------------------------------------------------------- | |||
776 | # Things related to hooks |
|
800 | # Things related to hooks | |
777 | #------------------------------------------------------------------------- |
|
801 | #------------------------------------------------------------------------- | |
778 |
|
802 | |||
@@ -1502,6 +1526,7 class InteractiveShell(SingletonConfigurable): | |||||
1502 | return 'not found' # so callers can take other action |
|
1526 | return 'not found' # so callers can take other action | |
1503 |
|
1527 | |||
1504 | def object_inspect(self, oname, detail_level=0): |
|
1528 | def object_inspect(self, oname, detail_level=0): | |
|
1529 | """Get object info about oname""" | |||
1505 | with self.builtin_trap: |
|
1530 | with self.builtin_trap: | |
1506 | info = self._object_find(oname) |
|
1531 | info = self._object_find(oname) | |
1507 | if info.found: |
|
1532 | if info.found: | |
@@ -1511,6 +1536,17 class InteractiveShell(SingletonConfigurable): | |||||
1511 | else: |
|
1536 | else: | |
1512 | return oinspect.object_info(name=oname, found=False) |
|
1537 | return oinspect.object_info(name=oname, found=False) | |
1513 |
|
1538 | |||
|
1539 | def object_inspect_text(self, oname, detail_level=0): | |||
|
1540 | """Get object info as formatted text""" | |||
|
1541 | with self.builtin_trap: | |||
|
1542 | info = self._object_find(oname) | |||
|
1543 | if info.found: | |||
|
1544 | return self.inspector._format_info(info.obj, oname, info=info, | |||
|
1545 | detail_level=detail_level | |||
|
1546 | ) | |||
|
1547 | else: | |||
|
1548 | raise KeyError(oname) | |||
|
1549 | ||||
1514 | #------------------------------------------------------------------------- |
|
1550 | #------------------------------------------------------------------------- | |
1515 | # Things related to history management |
|
1551 | # Things related to history management | |
1516 | #------------------------------------------------------------------------- |
|
1552 | #------------------------------------------------------------------------- | |
@@ -2395,7 +2431,7 class InteractiveShell(SingletonConfigurable): | |||||
2395 | def _user_obj_error(self): |
|
2431 | def _user_obj_error(self): | |
2396 | """return simple exception dict |
|
2432 | """return simple exception dict | |
2397 |
|
2433 | |||
2398 |
for use in user_ |
|
2434 | for use in user_expressions | |
2399 | """ |
|
2435 | """ | |
2400 |
|
2436 | |||
2401 | etype, evalue, tb = self._get_exc_info() |
|
2437 | etype, evalue, tb = self._get_exc_info() | |
@@ -2413,7 +2449,7 class InteractiveShell(SingletonConfigurable): | |||||
2413 | def _format_user_obj(self, obj): |
|
2449 | def _format_user_obj(self, obj): | |
2414 | """format a user object to display dict |
|
2450 | """format a user object to display dict | |
2415 |
|
2451 | |||
2416 |
for use in user_expressions |
|
2452 | for use in user_expressions | |
2417 | """ |
|
2453 | """ | |
2418 |
|
2454 | |||
2419 | data, md = self.display_formatter.format(obj) |
|
2455 | data, md = self.display_formatter.format(obj) | |
@@ -2424,30 +2460,6 class InteractiveShell(SingletonConfigurable): | |||||
2424 | } |
|
2460 | } | |
2425 | return value |
|
2461 | return value | |
2426 |
|
2462 | |||
2427 | def user_variables(self, names): |
|
|||
2428 | """Get a list of variable names from the user's namespace. |
|
|||
2429 |
|
||||
2430 | Parameters |
|
|||
2431 | ---------- |
|
|||
2432 | names : list of strings |
|
|||
2433 | A list of names of variables to be read from the user namespace. |
|
|||
2434 |
|
||||
2435 | Returns |
|
|||
2436 | ------- |
|
|||
2437 | A dict, keyed by the input names and with the rich mime-type repr(s) of each value. |
|
|||
2438 | Each element will be a sub-dict of the same form as a display_data message. |
|
|||
2439 | """ |
|
|||
2440 | out = {} |
|
|||
2441 | user_ns = self.user_ns |
|
|||
2442 |
|
||||
2443 | for varname in names: |
|
|||
2444 | try: |
|
|||
2445 | value = self._format_user_obj(user_ns[varname]) |
|
|||
2446 | except: |
|
|||
2447 | value = self._user_obj_error() |
|
|||
2448 | out[varname] = value |
|
|||
2449 | return out |
|
|||
2450 |
|
||||
2451 | def user_expressions(self, expressions): |
|
2463 | def user_expressions(self, expressions): | |
2452 | """Evaluate a dict of expressions in the user's namespace. |
|
2464 | """Evaluate a dict of expressions in the user's namespace. | |
2453 |
|
2465 |
@@ -7,12 +7,9 Similar in spirit to the inspect module, but all calls take a name argument to | |||||
7 | reference the name under which an object is being read. |
|
7 | reference the name under which an object is being read. | |
8 | """ |
|
8 | """ | |
9 |
|
9 | |||
10 | #***************************************************************************** |
|
10 | # Copyright (c) IPython Development Team. | |
11 | # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu> |
|
11 | # Distributed under the terms of the Modified BSD License. | |
12 | # |
|
12 | ||
13 | # Distributed under the terms of the BSD License. The full license is in |
|
|||
14 | # the file COPYING, distributed as part of this software. |
|
|||
15 | #***************************************************************************** |
|
|||
16 | from __future__ import print_function |
|
13 | from __future__ import print_function | |
17 |
|
14 | |||
18 | __all__ = ['Inspector','InspectColors'] |
|
15 | __all__ = ['Inspector','InspectColors'] | |
@@ -533,20 +530,8 class Inspector: | |||||
533 | ("Call def", "call_def"), |
|
530 | ("Call def", "call_def"), | |
534 | ("Call docstring", "call_docstring")] |
|
531 | ("Call docstring", "call_docstring")] | |
535 |
|
532 | |||
536 |
def |
|
533 | def _format_info(self, obj, oname='', formatter=None, info=None, detail_level=0): | |
537 | """Show detailed information about an object. |
|
534 | """Format an info dict as text""" | |
538 |
|
||||
539 | Optional arguments: |
|
|||
540 |
|
||||
541 | - oname: name of the variable pointing to the object. |
|
|||
542 |
|
||||
543 | - formatter: special formatter for docstrings (see pdoc) |
|
|||
544 |
|
||||
545 | - info: a structure with some information fields which may have been |
|
|||
546 | precomputed already. |
|
|||
547 |
|
||||
548 | - detail_level: if set to 1, more information is given. |
|
|||
549 | """ |
|
|||
550 | info = self.info(obj, oname=oname, formatter=formatter, |
|
535 | info = self.info(obj, oname=oname, formatter=formatter, | |
551 | info=info, detail_level=detail_level) |
|
536 | info=info, detail_level=detail_level) | |
552 | displayfields = [] |
|
537 | displayfields = [] | |
@@ -591,9 +576,28 class Inspector: | |||||
591 | else: |
|
576 | else: | |
592 | add_fields(self.pinfo_fields_obj) |
|
577 | add_fields(self.pinfo_fields_obj) | |
593 |
|
578 | |||
594 | # Finally send to printer/pager: |
|
|||
595 | if displayfields: |
|
579 | if displayfields: | |
596 |
|
|
580 | return self._format_fields(displayfields) | |
|
581 | else: | |||
|
582 | return u'' | |||
|
583 | ||||
|
584 | def pinfo(self, obj, oname='', formatter=None, info=None, detail_level=0): | |||
|
585 | """Show detailed information about an object. | |||
|
586 | ||||
|
587 | Optional arguments: | |||
|
588 | ||||
|
589 | - oname: name of the variable pointing to the object. | |||
|
590 | ||||
|
591 | - formatter: special formatter for docstrings (see pdoc) | |||
|
592 | ||||
|
593 | - info: a structure with some information fields which may have been | |||
|
594 | precomputed already. | |||
|
595 | ||||
|
596 | - detail_level: if set to 1, more information is given. | |||
|
597 | """ | |||
|
598 | text = self._format_info(obj, oname, formatter, info, detail_level) | |||
|
599 | if text: | |||
|
600 | page.page(text) | |||
597 |
|
601 | |||
598 | def info(self, obj, oname='', formatter=None, info=None, detail_level=0): |
|
602 | def info(self, obj, oname='', formatter=None, info=None, detail_level=0): | |
599 | """Compute a dict with detailed information about an object. |
|
603 | """Compute a dict with detailed information about an object. |
@@ -133,7 +133,10 def _detect_screen_size(screen_lines_def): | |||||
133 | #screen_cols,'columns.' # dbg |
|
133 | #screen_cols,'columns.' # dbg | |
134 |
|
134 | |||
135 | def page(strng, start=0, screen_lines=0, pager_cmd=None): |
|
135 | def page(strng, start=0, screen_lines=0, pager_cmd=None): | |
136 |
""" |
|
136 | """Display a string, piping through a pager after a certain length. | |
|
137 | ||||
|
138 | strng can be a mime-bundle dict, supplying multiple representations, | |||
|
139 | keyed by mime-type. | |||
137 |
|
140 | |||
138 | The screen_lines parameter specifies the number of *usable* lines of your |
|
141 | The screen_lines parameter specifies the number of *usable* lines of your | |
139 | terminal screen (total lines minus lines you need to reserve to show other |
|
142 | terminal screen (total lines minus lines you need to reserve to show other | |
@@ -153,6 +156,10 def page(strng, start=0, screen_lines=0, pager_cmd=None): | |||||
153 | written in python, very simplistic. |
|
156 | written in python, very simplistic. | |
154 | """ |
|
157 | """ | |
155 |
|
158 | |||
|
159 | # for compatibility with mime-bundle form: | |||
|
160 | if isinstance(strng, dict): | |||
|
161 | strng = strng['text/plain'] | |||
|
162 | ||||
156 | # Some routines may auto-compute start offsets incorrectly and pass a |
|
163 | # Some routines may auto-compute start offsets incorrectly and pass a | |
157 | # negative value. Offset to 0 for robustness. |
|
164 | # negative value. Offset to 0 for robustness. | |
158 | start = max(0, start) |
|
165 | start = max(0, start) |
@@ -1,41 +1,16 | |||||
1 | # encoding: utf-8 |
|
1 | # encoding: utf-8 | |
2 | """ |
|
2 | """A payload based version of page.""" | |
3 | A payload based version of page. |
|
|||
4 |
|
||||
5 | Authors: |
|
|||
6 |
|
3 | |||
7 | * Brian Granger |
|
4 | # Copyright (c) IPython Development Team. | |
8 | * Fernando Perez |
|
5 | # Distributed under the terms of the Modified BSD License. | |
9 | """ |
|
|||
10 |
|
6 | |||
11 | #----------------------------------------------------------------------------- |
|
7 | from IPython.core.getipython import get_ipython | |
12 | # Copyright (C) 2008-2011 The IPython Development Team |
|
|||
13 | # |
|
|||
14 | # Distributed under the terms of the BSD License. The full license is in |
|
|||
15 | # the file COPYING, distributed as part of this software. |
|
|||
16 | #----------------------------------------------------------------------------- |
|
|||
17 |
|
||||
18 | #----------------------------------------------------------------------------- |
|
|||
19 | # Imports |
|
|||
20 | #----------------------------------------------------------------------------- |
|
|||
21 |
|
||||
22 | # Third-party |
|
|||
23 | try: |
|
|||
24 | from docutils.core import publish_string |
|
|||
25 | except ImportError: |
|
|||
26 | # html paging won't be available, but we don't raise any errors. It's a |
|
|||
27 | # purely optional feature. |
|
|||
28 | pass |
|
|||
29 |
|
||||
30 | # Our own |
|
|||
31 | from IPython.core.interactiveshell import InteractiveShell |
|
|||
32 |
|
8 | |||
33 | #----------------------------------------------------------------------------- |
|
9 | #----------------------------------------------------------------------------- | |
34 | # Classes and functions |
|
10 | # Classes and functions | |
35 | #----------------------------------------------------------------------------- |
|
11 | #----------------------------------------------------------------------------- | |
36 |
|
12 | |||
37 |
def page(strng, start=0, screen_lines=0, pager_cmd=None |
|
13 | def page(strng, start=0, screen_lines=0, pager_cmd=None): | |
38 | html=None, auto_html=False): |
|
|||
39 | """Print a string, piping through a pager. |
|
14 | """Print a string, piping through a pager. | |
40 |
|
15 | |||
41 | This version ignores the screen_lines and pager_cmd arguments and uses |
|
16 | This version ignores the screen_lines and pager_cmd arguments and uses | |
@@ -43,49 +18,28 def page(strng, start=0, screen_lines=0, pager_cmd=None, | |||||
43 |
|
18 | |||
44 | Parameters |
|
19 | Parameters | |
45 | ---------- |
|
20 | ---------- | |
46 | strng : str |
|
21 | strng : str or mime-dict | |
47 | Text to page. |
|
22 | Text to page, or a mime-type keyed dict of already formatted data. | |
48 |
|
23 | |||
49 | start : int |
|
24 | start : int | |
50 | Starting line at which to place the display. |
|
25 | Starting line at which to place the display. | |
51 |
|
||||
52 | html : str, optional |
|
|||
53 | If given, an html string to send as well. |
|
|||
54 |
|
||||
55 | auto_html : bool, optional |
|
|||
56 | If true, the input string is assumed to be valid reStructuredText and is |
|
|||
57 | converted to HTML with docutils. Note that if docutils is not found, |
|
|||
58 | this option is silently ignored. |
|
|||
59 |
|
||||
60 | Notes |
|
|||
61 | ----- |
|
|||
62 |
|
||||
63 | Only one of the ``html`` and ``auto_html`` options can be given, not |
|
|||
64 | both. |
|
|||
65 | """ |
|
26 | """ | |
66 |
|
27 | |||
67 | # Some routines may auto-compute start offsets incorrectly and pass a |
|
28 | # Some routines may auto-compute start offsets incorrectly and pass a | |
68 | # negative value. Offset to 0 for robustness. |
|
29 | # negative value. Offset to 0 for robustness. | |
69 | start = max(0, start) |
|
30 | start = max(0, start) | |
70 | shell = InteractiveShell.instance() |
|
31 | shell = get_ipython() | |
71 |
|
||||
72 | if auto_html: |
|
|||
73 | try: |
|
|||
74 | # These defaults ensure user configuration variables for docutils |
|
|||
75 | # are not loaded, only our config is used here. |
|
|||
76 | defaults = {'file_insertion_enabled': 0, |
|
|||
77 | 'raw_enabled': 0, |
|
|||
78 | '_disable_config': 1} |
|
|||
79 | html = publish_string(strng, writer_name='html', |
|
|||
80 | settings_overrides=defaults) |
|
|||
81 | except: |
|
|||
82 | pass |
|
|||
83 |
|
|
32 | ||
|
33 | if isinstance(strng, dict): | |||
|
34 | data = strng | |||
|
35 | else: | |||
|
36 | data = {'text/plain' : strng} | |||
84 | payload = dict( |
|
37 | payload = dict( | |
85 | source='page', |
|
38 | source='page', | |
|
39 | data=data, | |||
86 | text=strng, |
|
40 | text=strng, | |
87 |
|
|
41 | start=start, | |
88 | start_line_number=start |
|
42 | screen_lines=screen_lines, | |
89 | ) |
|
43 | ) | |
90 | shell.payload_manager.write_payload(payload) |
|
44 | shell.payload_manager.write_payload(payload) | |
91 |
|
45 |
@@ -40,7 +40,8 version = __version__ # backwards compatibility name | |||||
40 | version_info = (_version_major, _version_minor, _version_patch, _version_extra) |
|
40 | version_info = (_version_major, _version_minor, _version_patch, _version_extra) | |
41 |
|
41 | |||
42 | # Change this when incrementing the kernel protocol version |
|
42 | # Change this when incrementing the kernel protocol version | |
43 |
kernel_protocol_version_info = ( |
|
43 | kernel_protocol_version_info = (5, 0) | |
|
44 | kernel_protocol_version = "%i.%i" % kernel_protocol_version_info | |||
44 |
|
45 | |||
45 | description = "IPython: Productive Interactive Computing" |
|
46 | description = "IPython: Productive Interactive Computing" | |
46 |
|
47 |
@@ -4,22 +4,11 | |||||
4 | Historically the main classes in interactiveshell have been under-tested. This |
|
4 | Historically the main classes in interactiveshell have been under-tested. This | |
5 | module should grow as many single-method tests as possible to trap many of the |
|
5 | module should grow as many single-method tests as possible to trap many of the | |
6 | recurring bugs we seem to encounter with high-level interaction. |
|
6 | recurring bugs we seem to encounter with high-level interaction. | |
7 |
|
||||
8 | Authors |
|
|||
9 | ------- |
|
|||
10 | * Fernando Perez |
|
|||
11 | """ |
|
7 | """ | |
12 | #----------------------------------------------------------------------------- |
|
|||
13 | # Copyright (C) 2011 The IPython Development Team |
|
|||
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 |
|
8 | |||
19 | #----------------------------------------------------------------------------- |
|
9 | # Copyright (c) IPython Development Team. | |
20 | # Imports |
|
10 | # Distributed under the terms of the Modified BSD License. | |
21 | #----------------------------------------------------------------------------- |
|
11 | ||
22 | # stdlib |
|
|||
23 | import ast |
|
12 | import ast | |
24 | import os |
|
13 | import os | |
25 | import signal |
|
14 | import signal | |
@@ -33,10 +22,8 except ImportError: | |||||
33 | import mock |
|
22 | import mock | |
34 | from os.path import join |
|
23 | from os.path import join | |
35 |
|
24 | |||
36 | # third-party |
|
|||
37 | import nose.tools as nt |
|
25 | import nose.tools as nt | |
38 |
|
26 | |||
39 | # Our own |
|
|||
40 | from IPython.core.inputtransformer import InputTransformer |
|
27 | from IPython.core.inputtransformer import InputTransformer | |
41 | from IPython.testing.decorators import skipif, skip_win32, onlyif_unicode_paths |
|
28 | from IPython.testing.decorators import skipif, skip_win32, onlyif_unicode_paths | |
42 | from IPython.testing import tools as tt |
|
29 | from IPython.testing import tools as tt | |
@@ -645,7 +632,7 def test_user_variables(): | |||||
645 |
|
632 | |||
646 | ip.user_ns['dummy'] = d = DummyRepr() |
|
633 | ip.user_ns['dummy'] = d = DummyRepr() | |
647 | keys = set(['dummy', 'doesnotexist']) |
|
634 | keys = set(['dummy', 'doesnotexist']) | |
648 | r = ip.user_variables(keys) |
|
635 | r = ip.user_expressions({ key:key for key in keys}) | |
649 |
|
636 | |||
650 | nt.assert_equal(keys, set(r.keys())) |
|
637 | nt.assert_equal(keys, set(r.keys())) | |
651 | dummy = r['dummy'] |
|
638 | dummy = r['dummy'] | |
@@ -660,7 +647,7 def test_user_variables(): | |||||
660 |
|
647 | |||
661 | dne = r['doesnotexist'] |
|
648 | dne = r['doesnotexist'] | |
662 | nt.assert_equal(dne['status'], 'error') |
|
649 | nt.assert_equal(dne['status'], 'error') | |
663 |
nt.assert_equal(dne['ename'], ' |
|
650 | nt.assert_equal(dne['ename'], 'NameError') | |
664 |
|
651 | |||
665 | # back to text only |
|
652 | # back to text only | |
666 | ip.display_formatter.active_types = ['text/plain'] |
|
653 | ip.display_formatter.active_types = ['text/plain'] |
@@ -1,13 +1,10 | |||||
1 | //---------------------------------------------------------------------------- |
|
1 | // Copyright (c) IPython Development Team. | |
2 | // Copyright (C) 2008-2012 The IPython Development Team |
|
2 | // Distributed under the terms of the Modified BSD License. | |
3 | // |
|
|||
4 | // Distributed under the terms of the BSD License. The full license is in |
|
|||
5 | // the file COPYING, distributed as part of this software. |
|
|||
6 | //---------------------------------------------------------------------------- |
|
|||
7 |
|
3 | |||
8 | //============================================================================ |
|
4 | //============================================================================ | |
9 | // Utilities |
|
5 | // Utilities | |
10 | //============================================================================ |
|
6 | //============================================================================ | |
|
7 | ||||
11 | IPython.namespace('IPython.utils'); |
|
8 | IPython.namespace('IPython.utils'); | |
12 |
|
9 | |||
13 | IPython.utils = (function (IPython) { |
|
10 | IPython.utils = (function (IPython) { | |
@@ -430,7 +427,7 IPython.utils = (function (IPython) { | |||||
430 | var escape_html = function (text) { |
|
427 | var escape_html = function (text) { | |
431 | // escape text to HTML |
|
428 | // escape text to HTML | |
432 | return $("<div/>").text(text).html(); |
|
429 | return $("<div/>").text(text).html(); | |
433 | } |
|
430 | }; | |
434 |
|
431 | |||
435 |
|
432 | |||
436 | var get_body_data = function(key) { |
|
433 | var get_body_data = function(key) { | |
@@ -440,6 +437,38 IPython.utils = (function (IPython) { | |||||
440 | return decodeURIComponent($('body').data(key)); |
|
437 | return decodeURIComponent($('body').data(key)); | |
441 | }; |
|
438 | }; | |
442 |
|
439 | |||
|
440 | var to_absolute_cursor_pos = function (cm, cursor) { | |||
|
441 | // get the absolute cursor position from CodeMirror's col, ch | |||
|
442 | if (!cursor) { | |||
|
443 | cursor = cm.getCursor(); | |||
|
444 | } | |||
|
445 | var cursor_pos = cursor.ch; | |||
|
446 | for (var i = 0; i < cursor.line; i++) { | |||
|
447 | cursor_pos += cm.getLine(i).length + 1; | |||
|
448 | } | |||
|
449 | return cursor_pos; | |||
|
450 | }; | |||
|
451 | ||||
|
452 | var from_absolute_cursor_pos = function (cm, cursor_pos) { | |||
|
453 | // turn absolute cursor postion into CodeMirror col, ch cursor | |||
|
454 | var i, line; | |||
|
455 | var offset = 0; | |||
|
456 | for (i = 0, line=cm.getLine(i); line !== undefined; i++, line=cm.getLine(i)) { | |||
|
457 | if (offset + line.length < cursor_pos) { | |||
|
458 | offset += line.length + 1; | |||
|
459 | } else { | |||
|
460 | return { | |||
|
461 | line : i, | |||
|
462 | ch : cursor_pos - offset, | |||
|
463 | }; | |||
|
464 | } | |||
|
465 | } | |||
|
466 | // reached end, return endpoint | |||
|
467 | return { | |||
|
468 | ch : line.length - 1, | |||
|
469 | line : i - 1, | |||
|
470 | }; | |||
|
471 | }; | |||
443 |
|
472 | |||
444 | // http://stackoverflow.com/questions/2400935/browser-detection-in-javascript |
|
473 | // http://stackoverflow.com/questions/2400935/browser-detection-in-javascript | |
445 | var browser = (function() { |
|
474 | var browser = (function() { | |
@@ -449,7 +478,7 IPython.utils = (function (IPython) { | |||||
449 | } |
|
478 | } | |
450 | var N= navigator.appName, ua= navigator.userAgent, tem; |
|
479 | var N= navigator.appName, ua= navigator.userAgent, tem; | |
451 | var M= ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i); |
|
480 | var M= ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i); | |
452 | if (M && (tem= ua.match(/version\/([\.\d]+)/i))!= null) M[2]= tem[1]; |
|
481 | if (M && (tem= ua.match(/version\/([\.\d]+)/i)) !== null) M[2]= tem[1]; | |
453 | M= M? [M[1], M[2]]: [N, navigator.appVersion,'-?']; |
|
482 | M= M? [M[1], M[2]]: [N, navigator.appVersion,'-?']; | |
454 | return M; |
|
483 | return M; | |
455 | })(); |
|
484 | })(); | |
@@ -465,13 +494,13 IPython.utils = (function (IPython) { | |||||
465 | if (navigator.appVersion.indexOf("Mac")!=-1) OSName="MacOS"; |
|
494 | if (navigator.appVersion.indexOf("Mac")!=-1) OSName="MacOS"; | |
466 | if (navigator.appVersion.indexOf("X11")!=-1) OSName="UNIX"; |
|
495 | if (navigator.appVersion.indexOf("X11")!=-1) OSName="UNIX"; | |
467 | if (navigator.appVersion.indexOf("Linux")!=-1) OSName="Linux"; |
|
496 | if (navigator.appVersion.indexOf("Linux")!=-1) OSName="Linux"; | |
468 | return OSName |
|
497 | return OSName; | |
469 | })(); |
|
498 | })(); | |
470 |
|
499 | |||
471 | var is_or_has = function (a, b) { |
|
500 | var is_or_has = function (a, b) { | |
472 | // Is b a child of a or a itself? |
|
501 | // Is b a child of a or a itself? | |
473 | return a.has(b).length !==0 || a.is(b); |
|
502 | return a.has(b).length !==0 || a.is(b); | |
474 | } |
|
503 | }; | |
475 |
|
504 | |||
476 | var is_focused = function (e) { |
|
505 | var is_focused = function (e) { | |
477 | // Is element e, or one of its children focused? |
|
506 | // Is element e, or one of its children focused? | |
@@ -486,7 +515,7 IPython.utils = (function (IPython) { | |||||
486 | } else { |
|
515 | } else { | |
487 | return false; |
|
516 | return false; | |
488 | } |
|
517 | } | |
489 | } |
|
518 | }; | |
490 |
|
519 | |||
491 | var log_ajax_error = function (jqXHR, status, error) { |
|
520 | var log_ajax_error = function (jqXHR, status, error) { | |
492 | // log ajax failures with informative messages |
|
521 | // log ajax failures with informative messages | |
@@ -515,6 +544,8 IPython.utils = (function (IPython) { | |||||
515 | splitext : splitext, |
|
544 | splitext : splitext, | |
516 | escape_html : escape_html, |
|
545 | escape_html : escape_html, | |
517 | always_new : always_new, |
|
546 | always_new : always_new, | |
|
547 | to_absolute_cursor_pos : to_absolute_cursor_pos, | |||
|
548 | from_absolute_cursor_pos : from_absolute_cursor_pos, | |||
518 | browser : browser, |
|
549 | browser : browser, | |
519 | platform: platform, |
|
550 | platform: platform, | |
520 | is_or_has : is_or_has, |
|
551 | is_or_has : is_or_has, |
@@ -1,21 +1,26 | |||||
1 | // function completer. |
|
1 | // Copyright (c) IPython Development Team. | |
|
2 | // Distributed under the terms of the Modified BSD License. | |||
|
3 | ||||
|
4 | // Completer | |||
2 | // |
|
5 | // | |
3 |
// |
|
6 | // Completer is be a class that takes a cell instance. | |
|
7 | ||||
4 | var IPython = (function (IPython) { |
|
8 | var IPython = (function (IPython) { | |
5 | // that will prevent us from misspelling |
|
9 | // that will prevent us from misspelling | |
6 | "use strict"; |
|
10 | "use strict"; | |
7 |
|
11 | |||
8 | // easier key mapping |
|
12 | // easier key mapping | |
9 | var keycodes = IPython.keyboard.keycodes; |
|
13 | var keycodes = IPython.keyboard.keycodes; | |
|
14 | var utils = IPython.utils; | |||
10 |
|
15 | |||
11 |
|
|
16 | var prepend_n_prc = function(str, n) { | |
12 | for( var i =0 ; i< n ; i++){ |
|
17 | for( var i =0 ; i< n ; i++){ | |
13 | str = '%'+str ; |
|
18 | str = '%'+str ; | |
14 | } |
|
19 | } | |
15 | return str; |
|
20 | return str; | |
16 | }; |
|
21 | }; | |
17 |
|
22 | |||
18 |
|
|
23 | var _existing_completion = function(item, completion_array){ | |
19 | for( var i=0; i < completion_array.length; i++) { |
|
24 | for( var i=0; i < completion_array.length; i++) { | |
20 | if (completion_array[i].trim().substr(-item.length) == item) { |
|
25 | if (completion_array[i].trim().substr(-item.length) == item) { | |
21 | return true; |
|
26 | return true; | |
@@ -143,13 +148,17 var IPython = (function (IPython) { | |||||
143 |
|
148 | |||
144 | // one kernel completion came back, finish_completing will be called with the results |
|
149 | // one kernel completion came back, finish_completing will be called with the results | |
145 | // we fork here and directly call finish completing if kernel is busy |
|
150 | // we fork here and directly call finish completing if kernel is busy | |
|
151 | var cursor_pos = utils.to_absolute_cursor_pos(this.editor, cur); | |||
146 | if (this.skip_kernel_completion) { |
|
152 | if (this.skip_kernel_completion) { | |
147 | this.finish_completing({ |
|
153 | this.finish_completing({ content: { | |
148 |
|
|
154 | matches: [], | |
149 | matched_text: "" |
|
155 | cursor_start: cursor_pos, | |
150 | }); |
|
156 | cursor_end: cursor_pos, | |
|
157 | }}); | |||
151 | } else { |
|
158 | } else { | |
152 | this.cell.kernel.complete(line, cur.ch, $.proxy(this.finish_completing, this)); |
|
159 | this.cell.kernel.complete(this.editor.getValue(), cursor_pos, | |
|
160 | $.proxy(this.finish_completing, this) | |||
|
161 | ); | |||
153 | } |
|
162 | } | |
154 | }; |
|
163 | }; | |
155 |
|
164 | |||
@@ -157,7 +166,8 var IPython = (function (IPython) { | |||||
157 | // let's build a function that wrap all that stuff into what is needed |
|
166 | // let's build a function that wrap all that stuff into what is needed | |
158 | // for the new completer: |
|
167 | // for the new completer: | |
159 | var content = msg.content; |
|
168 | var content = msg.content; | |
160 |
var |
|
169 | var start = content.cursor_start; | |
|
170 | var end = content.cursor_end; | |||
161 | var matches = content.matches; |
|
171 | var matches = content.matches; | |
162 |
|
172 | |||
163 | var cur = this.editor.getCursor(); |
|
173 | var cur = this.editor.getCursor(); | |
@@ -165,7 +175,8 var IPython = (function (IPython) { | |||||
165 | var filtered_results = []; |
|
175 | var filtered_results = []; | |
166 | //remove results from context completion |
|
176 | //remove results from context completion | |
167 | //that are already in kernel completion |
|
177 | //that are already in kernel completion | |
168 | for (var i=0; i < results.length; i++) { |
|
178 | var i; | |
|
179 | for (i=0; i < results.length; i++) { | |||
169 | if (!_existing_completion(results[i].str, matches)) { |
|
180 | if (!_existing_completion(results[i].str, matches)) { | |
170 | filtered_results.push(results[i]); |
|
181 | filtered_results.push(results[i]); | |
171 | } |
|
182 | } | |
@@ -174,18 +185,12 var IPython = (function (IPython) { | |||||
174 | // append the introspection result, in order, at at the beginning of |
|
185 | // append the introspection result, in order, at at the beginning of | |
175 | // the table and compute the replacement range from current cursor |
|
186 | // the table and compute the replacement range from current cursor | |
176 | // positon and matched_text length. |
|
187 | // positon and matched_text length. | |
177 |
for ( |
|
188 | for (i = matches.length - 1; i >= 0; --i) { | |
178 | filtered_results.unshift({ |
|
189 | filtered_results.unshift({ | |
179 | str: matches[i], |
|
190 | str: matches[i], | |
180 | type: "introspection", |
|
191 | type: "introspection", | |
181 | from: { |
|
192 | from: utils.from_absolute_cursor_pos(this.editor, start), | |
182 | line: cur.line, |
|
193 | to: utils.from_absolute_cursor_pos(this.editor, end) | |
183 | ch: cur.ch - matched_text.length |
|
|||
184 | }, |
|
|||
185 | to: { |
|
|||
186 | line: cur.line, |
|
|||
187 | ch: cur.ch |
|
|||
188 | } |
|
|||
189 | }); |
|
194 | }); | |
190 | } |
|
195 | } | |
191 |
|
196 | |||
@@ -249,8 +254,9 var IPython = (function (IPython) { | |||||
249 |
|
254 | |||
250 | // After everything is on the page, compute the postion. |
|
255 | // After everything is on the page, compute the postion. | |
251 | // We put it above the code if it is too close to the bottom of the page. |
|
256 | // We put it above the code if it is too close to the bottom of the page. | |
252 | cur.ch = cur.ch-matched_text.length; |
|
257 | var pos = this.editor.cursorCoords( | |
253 | var pos = this.editor.cursorCoords(cur); |
|
258 | utils.from_absolute_cursor_pos(this.editor, start) | |
|
259 | ); | |||
254 | var left = pos.left-3; |
|
260 | var left = pos.left-3; | |
255 | var top; |
|
261 | var top; | |
256 | var cheight = this.complete.height(); |
|
262 | var cheight = this.complete.height(); |
@@ -1,9 +1,5 | |||||
1 | //---------------------------------------------------------------------------- |
|
1 | // Copyright (c) IPython Development Team. | |
2 | // Copyright (C) 2008 The IPython Development Team |
|
2 | // Distributed under the terms of the Modified BSD License. | |
3 | // |
|
|||
4 | // Distributed under the terms of the BSD License. The full license is in |
|
|||
5 | // the file COPYING, distributed as part of this software. |
|
|||
6 | //---------------------------------------------------------------------------- |
|
|||
7 |
|
3 | |||
8 | //============================================================================ |
|
4 | //============================================================================ | |
9 | // OutputArea |
|
5 | // OutputArea | |
@@ -221,15 +217,18 var IPython = (function (IPython) { | |||||
221 | json = content.data; |
|
217 | json = content.data; | |
222 | json.output_type = msg_type; |
|
218 | json.output_type = msg_type; | |
223 | json.metadata = content.metadata; |
|
219 | json.metadata = content.metadata; | |
224 |
} else if (msg_type === " |
|
220 | } else if (msg_type === "execute_result") { | |
225 | json = content.data; |
|
221 | json = content.data; | |
226 | json.output_type = msg_type; |
|
222 | json.output_type = msg_type; | |
227 | json.metadata = content.metadata; |
|
223 | json.metadata = content.metadata; | |
228 | json.prompt_number = content.execution_count; |
|
224 | json.prompt_number = content.execution_count; | |
229 |
} else if (msg_type === " |
|
225 | } else if (msg_type === "error") { | |
230 | json.ename = content.ename; |
|
226 | json.ename = content.ename; | |
231 | json.evalue = content.evalue; |
|
227 | json.evalue = content.evalue; | |
232 | json.traceback = content.traceback; |
|
228 | json.traceback = content.traceback; | |
|
229 | } else { | |||
|
230 | console.log("unhandled output message", msg); | |||
|
231 | return; | |||
233 | } |
|
232 | } | |
234 | this.append_output(json); |
|
233 | this.append_output(json); | |
235 | }; |
|
234 | }; | |
@@ -283,10 +282,10 var IPython = (function (IPython) { | |||||
283 | needs_height_reset = true; |
|
282 | needs_height_reset = true; | |
284 | } |
|
283 | } | |
285 |
|
284 | |||
286 |
if (json.output_type === ' |
|
285 | if (json.output_type === 'execute_result') { | |
287 |
this.append_ |
|
286 | this.append_execute_result(json); | |
288 |
} else if (json.output_type === ' |
|
287 | } else if (json.output_type === 'error') { | |
289 |
this.append_ |
|
288 | this.append_error(json); | |
290 | } else if (json.output_type === 'stream') { |
|
289 | } else if (json.output_type === 'stream') { | |
291 | this.append_stream(json); |
|
290 | this.append_stream(json); | |
292 | } |
|
291 | } | |
@@ -406,7 +405,7 var IPython = (function (IPython) { | |||||
406 | }; |
|
405 | }; | |
407 |
|
406 | |||
408 |
|
407 | |||
409 |
OutputArea.prototype.append_ |
|
408 | OutputArea.prototype.append_execute_result = function (json) { | |
410 | var n = json.prompt_number || ' '; |
|
409 | var n = json.prompt_number || ' '; | |
411 | var toinsert = this.create_output_area(); |
|
410 | var toinsert = this.create_output_area(); | |
412 | if (this.prompt_area) { |
|
411 | if (this.prompt_area) { | |
@@ -414,7 +413,7 var IPython = (function (IPython) { | |||||
414 | } |
|
413 | } | |
415 | var inserted = this.append_mime_type(json, toinsert); |
|
414 | var inserted = this.append_mime_type(json, toinsert); | |
416 | if (inserted) { |
|
415 | if (inserted) { | |
417 |
inserted.addClass('output_ |
|
416 | inserted.addClass('output_result'); | |
418 | } |
|
417 | } | |
419 | this._safe_append(toinsert); |
|
418 | this._safe_append(toinsert); | |
420 | // If we just output latex, typeset it. |
|
419 | // If we just output latex, typeset it. | |
@@ -426,7 +425,7 var IPython = (function (IPython) { | |||||
426 | }; |
|
425 | }; | |
427 |
|
426 | |||
428 |
|
427 | |||
429 |
OutputArea.prototype.append_ |
|
428 | OutputArea.prototype.append_error = function (json) { | |
430 | var tb = json.traceback; |
|
429 | var tb = json.traceback; | |
431 | if (tb !== undefined && tb.length > 0) { |
|
430 | if (tb !== undefined && tb.length > 0) { | |
432 | var s = ''; |
|
431 | var s = ''; | |
@@ -438,7 +437,7 var IPython = (function (IPython) { | |||||
438 | var toinsert = this.create_output_area(); |
|
437 | var toinsert = this.create_output_area(); | |
439 | var append_text = OutputArea.append_map['text/plain']; |
|
438 | var append_text = OutputArea.append_map['text/plain']; | |
440 | if (append_text) { |
|
439 | if (append_text) { | |
441 |
append_text.apply(this, [s, {}, toinsert]).addClass('output_ |
|
440 | append_text.apply(this, [s, {}, toinsert]).addClass('output_error'); | |
442 | } |
|
441 | } | |
443 | this._safe_append(toinsert); |
|
442 | this._safe_append(toinsert); | |
444 | } |
|
443 | } | |
@@ -746,6 +745,8 var IPython = (function (IPython) { | |||||
746 | // disable any other raw_inputs, if they are left around |
|
745 | // disable any other raw_inputs, if they are left around | |
747 | $("div.output_subarea.raw_input_container").remove(); |
|
746 | $("div.output_subarea.raw_input_container").remove(); | |
748 |
|
747 | |||
|
748 | var input_type = content.password ? 'password' : 'text'; | |||
|
749 | ||||
749 | area.append( |
|
750 | area.append( | |
750 | $("<div/>") |
|
751 | $("<div/>") | |
751 | .addClass("box-flex1 output_subarea raw_input_container") |
|
752 | .addClass("box-flex1 output_subarea raw_input_container") | |
@@ -757,7 +758,7 var IPython = (function (IPython) { | |||||
757 | .append( |
|
758 | .append( | |
758 | $("<input/>") |
|
759 | $("<input/>") | |
759 | .addClass("raw_input") |
|
760 | .addClass("raw_input") | |
760 |
.attr('type', |
|
761 | .attr('type', input_type) | |
761 | .attr("size", 47) |
|
762 | .attr("size", 47) | |
762 | .keydown(function (event, ui) { |
|
763 | .keydown(function (event, ui) { | |
763 | // make sure we submit on enter, |
|
764 | // make sure we submit on enter, | |
@@ -786,10 +787,15 var IPython = (function (IPython) { | |||||
786 | var theprompt = container.find("span.raw_input_prompt"); |
|
787 | var theprompt = container.find("span.raw_input_prompt"); | |
787 | var theinput = container.find("input.raw_input"); |
|
788 | var theinput = container.find("input.raw_input"); | |
788 | var value = theinput.val(); |
|
789 | var value = theinput.val(); | |
|
790 | var echo = value; | |||
|
791 | // don't echo if it's a password | |||
|
792 | if (theinput.attr('type') == 'password') { | |||
|
793 | echo = '········'; | |||
|
794 | } | |||
789 | var content = { |
|
795 | var content = { | |
790 | output_type : 'stream', |
|
796 | output_type : 'stream', | |
791 | name : 'stdout', |
|
797 | name : 'stdout', | |
792 |
text : theprompt.text() + |
|
798 | text : theprompt.text() + echo + '\n' | |
793 | } |
|
799 | } | |
794 | // remove form container |
|
800 | // remove form container | |
795 | container.parent().remove(); |
|
801 | container.parent().remove(); | |
@@ -850,11 +856,26 var IPython = (function (IPython) { | |||||
850 | for (var i=0; i<len; i++) { |
|
856 | for (var i=0; i<len; i++) { | |
851 | data = outputs[i]; |
|
857 | data = outputs[i]; | |
852 | var msg_type = data.output_type; |
|
858 | var msg_type = data.output_type; | |
853 |
if (msg_type == |
|
859 | if (msg_type == "pyout") { | |
|
860 | // pyout message has been renamed to execute_result, | |||
|
861 | // but the nbformat has not been updated, | |||
|
862 | // so transform back to pyout for json. | |||
|
863 | msg_type = data.output_type = "execute_result"; | |||
|
864 | } else if (msg_type == "pyerr") { | |||
|
865 | // pyerr message has been renamed to error, | |||
|
866 | // but the nbformat has not been updated, | |||
|
867 | // so transform back to pyerr for json. | |||
|
868 | msg_type = data.output_type = "error"; | |||
|
869 | } | |||
|
870 | if (msg_type === "display_data" || msg_type === "execute_result") { | |||
854 | // convert short keys to mime keys |
|
871 | // convert short keys to mime keys | |
855 | // TODO: remove mapping of short keys when we update to nbformat 4 |
|
872 | // TODO: remove mapping of short keys when we update to nbformat 4 | |
856 | data = this.rename_keys(data, OutputArea.mime_map_r); |
|
873 | data = this.rename_keys(data, OutputArea.mime_map_r); | |
857 | data.metadata = this.rename_keys(data.metadata, OutputArea.mime_map_r); |
|
874 | data.metadata = this.rename_keys(data.metadata, OutputArea.mime_map_r); | |
|
875 | // msg spec JSON is an object, nbformat v3 JSON is a JSON string | |||
|
876 | if (data["application/json"] !== undefined && typeof data["application/json"] === 'string') { | |||
|
877 | data["application/json"] = JSON.parse(data["application/json"]); | |||
|
878 | } | |||
858 | } |
|
879 | } | |
859 |
|
880 | |||
860 | this.append_output(data); |
|
881 | this.append_output(data); | |
@@ -869,10 +890,25 var IPython = (function (IPython) { | |||||
869 | for (var i=0; i<len; i++) { |
|
890 | for (var i=0; i<len; i++) { | |
870 | data = this.outputs[i]; |
|
891 | data = this.outputs[i]; | |
871 | var msg_type = data.output_type; |
|
892 | var msg_type = data.output_type; | |
872 |
if (msg_type === "display_data" || msg_type === " |
|
893 | if (msg_type === "display_data" || msg_type === "execute_result") { | |
873 | // convert mime keys to short keys |
|
894 | // convert mime keys to short keys | |
874 | data = this.rename_keys(data, OutputArea.mime_map); |
|
895 | data = this.rename_keys(data, OutputArea.mime_map); | |
875 | data.metadata = this.rename_keys(data.metadata, OutputArea.mime_map); |
|
896 | data.metadata = this.rename_keys(data.metadata, OutputArea.mime_map); | |
|
897 | // msg spec JSON is an object, nbformat v3 JSON is a JSON string | |||
|
898 | if (data.json !== undefined && typeof data.json !== 'string') { | |||
|
899 | data.json = JSON.stringify(data.json); | |||
|
900 | } | |||
|
901 | } | |||
|
902 | if (msg_type == "execute_result") { | |||
|
903 | // pyout message has been renamed to execute_result, | |||
|
904 | // but the nbformat has not been updated, | |||
|
905 | // so transform back to pyout for json. | |||
|
906 | data.output_type = "pyout"; | |||
|
907 | } else if (msg_type == "error") { | |||
|
908 | // pyerr message has been renamed to error, | |||
|
909 | // but the nbformat has not been updated, | |||
|
910 | // so transform back to pyerr for json. | |||
|
911 | data.output_type = "pyerr"; | |||
876 | } |
|
912 | } | |
877 | outputs[i] = data; |
|
913 | outputs[i] = data; | |
878 | } |
|
914 | } |
@@ -1,9 +1,5 | |||||
1 | //---------------------------------------------------------------------------- |
|
1 | // Copyright (c) IPython Development Team. | |
2 | // Copyright (C) 2008-2011 The IPython Development Team |
|
2 | // Distributed under the terms of the Modified BSD License. | |
3 | // |
|
|||
4 | // Distributed under the terms of the BSD License. The full license is in |
|
|||
5 | // the file COPYING, distributed as part of this software. |
|
|||
6 | //---------------------------------------------------------------------------- |
|
|||
7 |
|
3 | |||
8 | //============================================================================ |
|
4 | //============================================================================ | |
9 | // Pager |
|
5 | // Pager | |
@@ -81,12 +77,18 var IPython = (function (IPython) { | |||||
81 | var that = this; |
|
77 | var that = this; | |
82 |
|
78 | |||
83 | this.pager_element.bind('collapse_pager', function (event, extrap) { |
|
79 | this.pager_element.bind('collapse_pager', function (event, extrap) { | |
84 | var time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast'; |
|
80 | var time = 'fast'; | |
|
81 | if (extrap && extrap.duration) { | |||
|
82 | time = extrap.duration; | |||
|
83 | } | |||
85 | that.pager_element.hide(time); |
|
84 | that.pager_element.hide(time); | |
86 | }); |
|
85 | }); | |
87 |
|
86 | |||
88 | this.pager_element.bind('expand_pager', function (event, extrap) { |
|
87 | this.pager_element.bind('expand_pager', function (event, extrap) { | |
89 | var time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast'; |
|
88 | var time = 'fast'; | |
|
89 | if (extrap && extrap.duration) { | |||
|
90 | time = extrap.duration; | |||
|
91 | } | |||
90 | that.pager_element.show(time); |
|
92 | that.pager_element.show(time); | |
91 | }); |
|
93 | }); | |
92 |
|
94 | |||
@@ -103,12 +105,13 var IPython = (function (IPython) { | |||||
103 | that.toggle(); |
|
105 | that.toggle(); | |
104 | }); |
|
106 | }); | |
105 |
|
107 | |||
106 |
$([IPython.events]).on('open_with_text.Pager', function (event, |
|
108 | $([IPython.events]).on('open_with_text.Pager', function (event, payload) { | |
107 | if (data.text.trim() !== '') { |
|
109 | // FIXME: support other mime types | |
|
110 | if (payload.data['text/plain'] && payload.data['text/plain'] !== "") { | |||
108 | that.clear(); |
|
111 | that.clear(); | |
109 | that.expand(); |
|
112 | that.expand(); | |
110 |
that.append_text(data |
|
113 | that.append_text(payload.data['text/plain']); | |
111 |
} |
|
114 | } | |
112 | }); |
|
115 | }); | |
113 | }; |
|
116 | }; | |
114 |
|
117 | |||
@@ -117,7 +120,7 var IPython = (function (IPython) { | |||||
117 | if (this.expanded === true) { |
|
120 | if (this.expanded === true) { | |
118 | this.expanded = false; |
|
121 | this.expanded = false; | |
119 | this.pager_element.add($('div#notebook')).trigger('collapse_pager', extrap); |
|
122 | this.pager_element.add($('div#notebook')).trigger('collapse_pager', extrap); | |
120 |
} |
|
123 | } | |
121 | }; |
|
124 | }; | |
122 |
|
125 | |||
123 |
|
126 | |||
@@ -125,7 +128,7 var IPython = (function (IPython) { | |||||
125 | if (this.expanded !== true) { |
|
128 | if (this.expanded !== true) { | |
126 | this.expanded = true; |
|
129 | this.expanded = true; | |
127 | this.pager_element.add($('div#notebook')).trigger('expand_pager', extrap); |
|
130 | this.pager_element.add($('div#notebook')).trigger('expand_pager', extrap); | |
128 |
} |
|
131 | } | |
129 | }; |
|
132 | }; | |
130 |
|
133 | |||
131 |
|
134 | |||
@@ -134,7 +137,7 var IPython = (function (IPython) { | |||||
134 | this.collapse(); |
|
137 | this.collapse(); | |
135 | } else { |
|
138 | } else { | |
136 | this.expand(); |
|
139 | this.expand(); | |
137 |
} |
|
140 | } | |
138 | }; |
|
141 | }; | |
139 |
|
142 | |||
140 |
|
143 | |||
@@ -160,8 +163,7 var IPython = (function (IPython) { | |||||
160 | pager_body.append(this.pager_element.clone().children()); |
|
163 | pager_body.append(this.pager_element.clone().children()); | |
161 | w.document.close(); |
|
164 | w.document.close(); | |
162 | this.collapse(); |
|
165 | this.collapse(); | |
163 |
|
166 | }; | ||
164 | } |
|
|||
165 |
|
167 | |||
166 | Pager.prototype.append_text = function (text) { |
|
168 | Pager.prototype.append_text = function (text) { | |
167 | // The only user content injected with this HTML call is escaped by |
|
169 | // The only user content injected with this HTML call is escaped by |
@@ -1,9 +1,6 | |||||
1 | //---------------------------------------------------------------------------- |
|
1 | // Copyright (c) IPython Development Team. | |
2 | // Copyright (C) 2008-2011 The IPython Development Team |
|
2 | // Distributed under the terms of the Modified BSD License. | |
3 | // |
|
3 | ||
4 | // Distributed under the terms of the BSD License. The full license is in |
|
|||
5 | // the file COPYING, distributed as part of this software. |
|
|||
6 | //---------------------------------------------------------------------------- |
|
|||
7 | //============================================================================ |
|
4 | //============================================================================ | |
8 | // Tooltip |
|
5 | // Tooltip | |
9 | //============================================================================ |
|
6 | //============================================================================ | |
@@ -105,8 +102,8 var IPython = (function (IPython) { | |||||
105 | this.tooltip.append(this.text); |
|
102 | this.tooltip.append(this.text); | |
106 |
|
103 | |||
107 | // function that will be called if you press tab 1, 2, 3... times in a row |
|
104 | // function that will be called if you press tab 1, 2, 3... times in a row | |
108 | this.tabs_functions = [function (cell, text) { |
|
105 | this.tabs_functions = [function (cell, text, cursor) { | |
109 | that._request_tooltip(cell, text); |
|
106 | that._request_tooltip(cell, text, cursor); | |
110 | }, function () { |
|
107 | }, function () { | |
111 | that.expand(); |
|
108 | that.expand(); | |
112 | }, function () { |
|
109 | }, function () { | |
@@ -131,13 +128,10 var IPython = (function (IPython) { | |||||
131 | Tooltip.prototype.showInPager = function (cell) { |
|
128 | Tooltip.prototype.showInPager = function (cell) { | |
132 | // reexecute last call in pager by appending ? to show back in pager |
|
129 | // reexecute last call in pager by appending ? to show back in pager | |
133 | var that = this; |
|
130 | var that = this; | |
134 |
var |
|
131 | var payload = {}; | |
135 | 'payload' : { |
|
132 | payload.text = that._reply.content.data['text/plain']; | |
136 | 'page' : $.proxy(cell._open_with_pager, cell) |
|
133 | ||
137 | } |
|
134 | $([IPython.events]).trigger('open_with_text.Pager', payload); | |
138 | } |
|
|||
139 | }; |
|
|||
140 | cell.kernel.execute(that.name + '?', callbacks, {'silent': false, 'store_history': true}); |
|
|||
141 | this.remove_and_cancel_tooltip(); |
|
135 | this.remove_and_cancel_tooltip(); | |
142 | }; |
|
136 | }; | |
143 |
|
137 | |||
@@ -222,10 +216,9 var IPython = (function (IPython) { | |||||
222 | return Tooltip.last_token_re.exec(line); |
|
216 | return Tooltip.last_token_re.exec(line); | |
223 | }; |
|
217 | }; | |
224 |
|
218 | |||
225 |
Tooltip.prototype._request_tooltip = function (cell, |
|
219 | Tooltip.prototype._request_tooltip = function (cell, text, cursor_pos) { | |
226 | var callbacks = $.proxy(this._show, this); |
|
220 | var callbacks = $.proxy(this._show, this); | |
227 | var oir_token = this.extract_oir_token(line); |
|
221 | var msg_id = cell.kernel.inspect(text, cursor_pos, callbacks); | |
228 | var msg_id = cell.kernel.object_info(oir_token, callbacks); |
|
|||
229 | }; |
|
222 | }; | |
230 |
|
223 | |||
231 | // make an imediate completion request |
|
224 | // make an imediate completion request | |
@@ -236,10 +229,8 var IPython = (function (IPython) { | |||||
236 | this.cancel_pending(); |
|
229 | this.cancel_pending(); | |
237 | var editor = cell.code_mirror; |
|
230 | var editor = cell.code_mirror; | |
238 | var cursor = editor.getCursor(); |
|
231 | var cursor = editor.getCursor(); | |
239 | var text = editor.getRange({ |
|
232 | var cursor_pos = utils.to_absolute_cursor_pos(editor, cursor); | |
240 | line: cursor.line, |
|
233 | var text = cell.get_text(); | |
241 | ch: 0 |
|
|||
242 | }, cursor).trim(); |
|
|||
243 |
|
234 | |||
244 | this._hide_if_no_docstring = hide_if_no_docstring; |
|
235 | this._hide_if_no_docstring = hide_if_no_docstring; | |
245 |
|
236 | |||
@@ -260,16 +251,11 var IPython = (function (IPython) { | |||||
260 | this.reset_tabs_function (cell, text); |
|
251 | this.reset_tabs_function (cell, text); | |
261 | } |
|
252 | } | |
262 |
|
253 | |||
263 | // don't do anything if line beggin with '(' or is empty |
|
254 | this.tabs_functions[this._consecutive_counter](cell, text, cursor_pos); | |
264 | if (text === "" || text === "(") { |
|
|||
265 | return; |
|
|||
266 | } |
|
|||
267 |
|
||||
268 | this.tabs_functions[this._consecutive_counter](cell, text); |
|
|||
269 |
|
255 | |||
270 | // then if we are at the end of list function, reset |
|
256 | // then if we are at the end of list function, reset | |
271 | if (this._consecutive_counter == this.tabs_functions.length) { |
|
257 | if (this._consecutive_counter == this.tabs_functions.length) { | |
272 | this.reset_tabs_function (cell, text); |
|
258 | this.reset_tabs_function (cell, text, cursor); | |
273 | } |
|
259 | } | |
274 |
|
260 | |||
275 | return; |
|
261 | return; | |
@@ -302,6 +288,7 var IPython = (function (IPython) { | |||||
302 | Tooltip.prototype._show = function (reply) { |
|
288 | Tooltip.prototype._show = function (reply) { | |
303 | // move the bubble if it is not hidden |
|
289 | // move the bubble if it is not hidden | |
304 | // otherwise fade it |
|
290 | // otherwise fade it | |
|
291 | this._reply = reply; | |||
305 | var content = reply.content; |
|
292 | var content = reply.content; | |
306 | if (!content.found) { |
|
293 | if (!content.found) { | |
307 | // object not found, nothing to show |
|
294 | // object not found, nothing to show | |
@@ -339,42 +326,13 var IPython = (function (IPython) { | |||||
339 | 'left': posarrowleft + 'px' |
|
326 | 'left': posarrowleft + 'px' | |
340 | }); |
|
327 | }); | |
341 |
|
328 | |||
342 | // build docstring |
|
|||
343 | var defstring = content.call_def; |
|
|||
344 | if (!defstring) { |
|
|||
345 | defstring = content.init_definition; |
|
|||
346 | } |
|
|||
347 | if (!defstring) { |
|
|||
348 | defstring = content.definition; |
|
|||
349 | } |
|
|||
350 |
|
||||
351 | var docstring = content.call_docstring; |
|
|||
352 | if (!docstring) { |
|
|||
353 | docstring = content.init_docstring; |
|
|||
354 | } |
|
|||
355 | if (!docstring) { |
|
|||
356 | docstring = content.docstring; |
|
|||
357 | } |
|
|||
358 |
|
||||
359 | if (!docstring) { |
|
|||
360 | // For reals this time, no docstring |
|
|||
361 | if (this._hide_if_no_docstring) { |
|
|||
362 | return; |
|
|||
363 | } else { |
|
|||
364 | docstring = "<empty docstring>"; |
|
|||
365 | } |
|
|||
366 | } |
|
|||
367 |
|
||||
368 | this._hidden = false; |
|
329 | this._hidden = false; | |
369 | this.tooltip.fadeIn('fast'); |
|
330 | this.tooltip.fadeIn('fast'); | |
370 | this.text.children().remove(); |
|
331 | this.text.children().remove(); | |
371 |
|
332 | |||
|
333 | // This should support rich data types, but only text/plain for now | |||
372 | // Any HTML within the docstring is escaped by the fixConsole() method. |
|
334 | // Any HTML within the docstring is escaped by the fixConsole() method. | |
373 |
var pre = $('<pre/>').html(utils.fixConsole( |
|
335 | var pre = $('<pre/>').html(utils.fixConsole(content.data['text/plain'])); | |
374 | if (defstring) { |
|
|||
375 | var defstring_html = $('<pre/>').html(utils.fixConsole(defstring)); |
|
|||
376 | this.text.append(defstring_html); |
|
|||
377 | } |
|
|||
378 | this.text.append(pre); |
|
336 | this.text.append(pre); | |
379 | // keep scroll top to be sure to always see the first line |
|
337 | // keep scroll top to be sure to always see the first line | |
380 | this.text.scrollTop(0); |
|
338 | this.text.scrollTop(0); |
@@ -107,7 +107,7 div.output_text { | |||||
107 | line-height: @code_line_height; |
|
107 | line-height: @code_line_height; | |
108 | } |
|
108 | } | |
109 |
|
109 | |||
110 |
/* stdout/stderr are 'text' as well as 'stream', but |
|
110 | /* stdout/stderr are 'text' as well as 'stream', but execute_result/error are *not* streams */ | |
111 | div.output_stream { |
|
111 | div.output_stream { | |
112 | } |
|
112 | } | |
113 |
|
113 |
@@ -57,7 +57,8 var IPython = (function (IPython) { | |||||
57 | msg_id : utils.uuid(), |
|
57 | msg_id : utils.uuid(), | |
58 | username : this.username, |
|
58 | username : this.username, | |
59 | session : this.session_id, |
|
59 | session : this.session_id, | |
60 | msg_type : msg_type |
|
60 | msg_type : msg_type, | |
|
61 | version : "5.0" | |||
61 | }, |
|
62 | }, | |
62 | metadata : metadata || {}, |
|
63 | metadata : metadata || {}, | |
63 | content : content, |
|
64 | content : content, | |
@@ -76,13 +77,13 var IPython = (function (IPython) { | |||||
76 | // Initialize the iopub handlers |
|
77 | // Initialize the iopub handlers | |
77 |
|
78 | |||
78 | Kernel.prototype.init_iopub_handlers = function () { |
|
79 | Kernel.prototype.init_iopub_handlers = function () { | |
79 |
var output_types = ['stream', 'display_data', ' |
|
80 | var output_msg_types = ['stream', 'display_data', 'execute_result', 'error']; | |
80 | this._iopub_handlers = {}; |
|
81 | this._iopub_handlers = {}; | |
81 | this.register_iopub_handler('status', $.proxy(this._handle_status_message, this)); |
|
82 | this.register_iopub_handler('status', $.proxy(this._handle_status_message, this)); | |
82 | this.register_iopub_handler('clear_output', $.proxy(this._handle_clear_output, this)); |
|
83 | this.register_iopub_handler('clear_output', $.proxy(this._handle_clear_output, this)); | |
83 |
|
84 | |||
84 | for (var i=0; i < output_types.length; i++) { |
|
85 | for (var i=0; i < output_msg_types.length; i++) { | |
85 | this.register_iopub_handler(output_types[i], $.proxy(this._handle_output_message, this)); |
|
86 | this.register_iopub_handler(output_msg_types[i], $.proxy(this._handle_output_message, this)); | |
86 | } |
|
87 | } | |
87 | }; |
|
88 | }; | |
88 |
|
89 | |||
@@ -246,7 +247,7 var IPython = (function (IPython) { | |||||
246 | * Get kernel info |
|
247 | * Get kernel info | |
247 | * |
|
248 | * | |
248 | * @param callback {function} |
|
249 | * @param callback {function} | |
249 |
* @method |
|
250 | * @method kernel_info | |
250 | * |
|
251 | * | |
251 | * When calling this method, pass a callback function that expects one argument. |
|
252 | * When calling this method, pass a callback function that expects one argument. | |
252 | * The callback will be passed the complete `kernel_info_reply` message documented |
|
253 | * The callback will be passed the complete `kernel_info_reply` message documented | |
@@ -263,28 +264,27 var IPython = (function (IPython) { | |||||
263 | /** |
|
264 | /** | |
264 | * Get info on an object |
|
265 | * Get info on an object | |
265 | * |
|
266 | * | |
266 |
* @param |
|
267 | * @param code {string} | |
|
268 | * @param cursor_pos {integer} | |||
267 | * @param callback {function} |
|
269 | * @param callback {function} | |
268 |
* @method |
|
270 | * @method inspect | |
269 | * |
|
271 | * | |
270 | * When calling this method, pass a callback function that expects one argument. |
|
272 | * When calling this method, pass a callback function that expects one argument. | |
271 |
* The callback will be passed the complete ` |
|
273 | * The callback will be passed the complete `inspect_reply` message documented | |
272 | * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#object-information) |
|
274 | * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#object-information) | |
273 | */ |
|
275 | */ | |
274 |
Kernel.prototype. |
|
276 | Kernel.prototype.inspect = function (code, cursor_pos, callback) { | |
275 | var callbacks; |
|
277 | var callbacks; | |
276 | if (callback) { |
|
278 | if (callback) { | |
277 | callbacks = { shell : { reply : callback } }; |
|
279 | callbacks = { shell : { reply : callback } }; | |
278 | } |
|
280 | } | |
279 |
|
281 | |||
280 | if (typeof(objname) !== null && objname !== null) { |
|
|||
281 |
|
|
282 | var content = { | |
282 | oname : objname.toString(), |
|
283 | code : code, | |
|
284 | cursor_pos : cursor_pos, | |||
283 |
|
|
285 | detail_level : 0, | |
284 |
|
|
286 | }; | |
285 |
|
|
287 | return this.send_shell_message("inspect_request", content, callbacks); | |
286 | } |
|
|||
287 | return; |
|
|||
288 | }; |
|
288 | }; | |
289 |
|
289 | |||
290 | /** |
|
290 | /** | |
@@ -302,7 +302,6 var IPython = (function (IPython) { | |||||
302 | * @param {object} [options] |
|
302 | * @param {object} [options] | |
303 | * @param [options.silent=false] {Boolean} |
|
303 | * @param [options.silent=false] {Boolean} | |
304 | * @param [options.user_expressions=empty_dict] {Dict} |
|
304 | * @param [options.user_expressions=empty_dict] {Dict} | |
305 | * @param [options.user_variables=empty_list] {List od Strings} |
|
|||
306 | * @param [options.allow_stdin=false] {Boolean} true|false |
|
305 | * @param [options.allow_stdin=false] {Boolean} true|false | |
307 | * |
|
306 | * | |
308 | * @example |
|
307 | * @example | |
@@ -312,7 +311,6 var IPython = (function (IPython) { | |||||
312 | * |
|
311 | * | |
313 | * options = { |
|
312 | * options = { | |
314 | * silent : true, |
|
313 | * silent : true, | |
315 | * user_variables : [], |
|
|||
316 | * user_expressions : {}, |
|
314 | * user_expressions : {}, | |
317 | * allow_stdin : false |
|
315 | * allow_stdin : false | |
318 | * } |
|
316 | * } | |
@@ -342,7 +340,6 var IPython = (function (IPython) { | |||||
342 | code : code, |
|
340 | code : code, | |
343 | silent : true, |
|
341 | silent : true, | |
344 | store_history : false, |
|
342 | store_history : false, | |
345 | user_variables : [], |
|
|||
346 | user_expressions : {}, |
|
343 | user_expressions : {}, | |
347 | allow_stdin : false |
|
344 | allow_stdin : false | |
348 | }; |
|
345 | }; | |
@@ -363,21 +360,19 var IPython = (function (IPython) { | |||||
363 | * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#complete) |
|
360 | * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#complete) | |
364 | * |
|
361 | * | |
365 | * @method complete |
|
362 | * @method complete | |
366 |
* @param |
|
363 | * @param code {string} | |
367 | * @param cursor_pos {integer} |
|
364 | * @param cursor_pos {integer} | |
368 | * @param callback {function} |
|
365 | * @param callback {function} | |
369 | * |
|
366 | * | |
370 | */ |
|
367 | */ | |
371 |
Kernel.prototype.complete = function ( |
|
368 | Kernel.prototype.complete = function (code, cursor_pos, callback) { | |
372 | var callbacks; |
|
369 | var callbacks; | |
373 | if (callback) { |
|
370 | if (callback) { | |
374 | callbacks = { shell : { reply : callback } }; |
|
371 | callbacks = { shell : { reply : callback } }; | |
375 | } |
|
372 | } | |
376 | var content = { |
|
373 | var content = { | |
377 |
|
|
374 | code : code, | |
378 | line : line, |
|
375 | cursor_pos : cursor_pos, | |
379 | block : null, |
|
|||
380 | cursor_pos : cursor_pos |
|
|||
381 | }; |
|
376 | }; | |
382 | return this.send_shell_message("complete_request", content, callbacks); |
|
377 | return this.send_shell_message("complete_request", content, callbacks); | |
383 | }; |
|
378 | }; | |
@@ -573,7 +568,7 var IPython = (function (IPython) { | |||||
573 | }; |
|
568 | }; | |
574 |
|
569 | |||
575 |
|
570 | |||
576 |
// handle an output message ( |
|
571 | // handle an output message (execute_result, display_data, etc.) | |
577 | Kernel.prototype._handle_output_message = function (msg) { |
|
572 | Kernel.prototype._handle_output_message = function (msg) { | |
578 | var callbacks = this.get_callbacks_for_msg(msg.parent_header.msg_id); |
|
573 | var callbacks = this.get_callbacks_for_msg(msg.parent_header.msg_id); | |
579 | if (!callbacks || !callbacks.iopub) { |
|
574 | if (!callbacks || !callbacks.iopub) { |
@@ -14,10 +14,9 b64_image_data = { | |||||
14 |
|
14 | |||
15 |
|
15 | |||
16 | casper.notebook_test(function () { |
|
16 | casper.notebook_test(function () { | |
17 | // this.printLog(); |
|
|||
18 | this.test_img_shape = function(fmt, retina) { |
|
17 | this.test_img_shape = function(fmt, retina) { | |
19 | this.thenEvaluate(function (b64data, retina) { |
|
18 | this.thenEvaluate(function (b64data, retina) { | |
20 |
IPython.notebook. |
|
19 | IPython.notebook.insert_cell_at_index(0, "code"); | |
21 | var cell = IPython.notebook.get_cell(0); |
|
20 | var cell = IPython.notebook.get_cell(0); | |
22 | cell.set_text([ |
|
21 | cell.set_text([ | |
23 | "import base64", |
|
22 | "import base64", |
@@ -33,7 +33,7 function assert_has(short_name, json, result, result2) { | |||||
33 | } |
|
33 | } | |
34 |
|
34 | |||
35 | // helper function for checkout that the first two cells have a particular |
|
35 | // helper function for checkout that the first two cells have a particular | |
36 |
// output_type (either ' |
|
36 | // output_type (either 'execute_result' or 'display_data'), and checks the to/fromJSON | |
37 | // for a set of mimetype keys, using their short names ('javascript', 'text', |
|
37 | // for a set of mimetype keys, using their short names ('javascript', 'text', | |
38 | // 'png', etc). |
|
38 | // 'png', etc). | |
39 | function check_output_area(output_type, keys) { |
|
39 | function check_output_area(output_type, keys) { | |
@@ -109,7 +109,7 casper.notebook_test(function () { | |||||
109 | }); |
|
109 | }); | |
110 |
|
110 | |||
111 | this.then(function () { |
|
111 | this.then(function () { | |
112 |
check_output_area.apply(this, [' |
|
112 | check_output_area.apply(this, ['execute_result', ['text', 'json']]); | |
113 | }); |
|
113 | }); | |
114 |
|
114 | |||
115 | this.then(function() { |
|
115 | this.then(function() { | |
@@ -127,7 +127,7 casper.notebook_test(function () { | |||||
127 | }); |
|
127 | }); | |
128 |
|
128 | |||
129 | this.then(function ( ) { |
|
129 | this.then(function ( ) { | |
130 |
check_output_area.apply(this, [' |
|
130 | check_output_area.apply(this, ['execute_result', ['text', 'latex']]); | |
131 | }); |
|
131 | }); | |
132 |
|
132 | |||
133 | this.then(function() { |
|
133 | this.then(function() { | |
@@ -145,7 +145,7 casper.notebook_test(function () { | |||||
145 | }); |
|
145 | }); | |
146 |
|
146 | |||
147 | this.then(function ( ) { |
|
147 | this.then(function ( ) { | |
148 |
check_output_area.apply(this, [' |
|
148 | check_output_area.apply(this, ['execute_result', ['text', 'html']]); | |
149 | }); |
|
149 | }); | |
150 |
|
150 | |||
151 | this.then(function() { |
|
151 | this.then(function() { | |
@@ -165,7 +165,7 casper.notebook_test(function () { | |||||
165 | this.thenEvaluate(function() { IPython.notebook.save_notebook(); }); |
|
165 | this.thenEvaluate(function() { IPython.notebook.save_notebook(); }); | |
166 |
|
166 | |||
167 | this.then(function ( ) { |
|
167 | this.then(function ( ) { | |
168 |
check_output_area.apply(this, [' |
|
168 | check_output_area.apply(this, ['execute_result', ['text', 'png']]); | |
169 | }); |
|
169 | }); | |
170 |
|
170 | |||
171 | this.then(function() { |
|
171 | this.then(function() { | |
@@ -184,7 +184,7 casper.notebook_test(function () { | |||||
184 | }); |
|
184 | }); | |
185 |
|
185 | |||
186 | this.then(function ( ) { |
|
186 | this.then(function ( ) { | |
187 |
check_output_area.apply(this, [' |
|
187 | check_output_area.apply(this, ['execute_result', ['text', 'jpeg']]); | |
188 | }); |
|
188 | }); | |
189 |
|
189 | |||
190 | this.then(function() { |
|
190 | this.then(function() { | |
@@ -202,7 +202,7 casper.notebook_test(function () { | |||||
202 | }); |
|
202 | }); | |
203 |
|
203 | |||
204 | this.then(function ( ) { |
|
204 | this.then(function ( ) { | |
205 |
check_output_area.apply(this, [' |
|
205 | check_output_area.apply(this, ['execute_result', ['text', 'svg']]); | |
206 | }); |
|
206 | }); | |
207 |
|
207 | |||
208 | this.then(function() { |
|
208 | this.then(function() { | |
@@ -238,7 +238,7 casper.notebook_test(function () { | |||||
238 | 'display_data custom mimetype ' + long_name); |
|
238 | 'display_data custom mimetype ' + long_name); | |
239 | var result = this.get_output_cell(0, 1); |
|
239 | var result = this.get_output_cell(0, 1); | |
240 | this.test.assertTrue(result.hasOwnProperty(long_name), |
|
240 | this.test.assertTrue(result.hasOwnProperty(long_name), | |
241 |
' |
|
241 | 'execute_result custom mimetype ' + long_name); | |
242 |
|
242 | |||
243 | }); |
|
243 | }); | |
244 |
|
244 |
@@ -14,7 +14,7 casper.notebook_test(function () { | |||||
14 | this.evaluate(function () { |
|
14 | this.evaluate(function () { | |
15 | var cell = IPython.notebook.get_cell(0); |
|
15 | var cell = IPython.notebook.get_cell(0); | |
16 | cell.set_text( "dp = get_ipython().display_pub\n" + |
|
16 | cell.set_text( "dp = get_ipython().display_pub\n" + | |
17 |
"dp.publish( |
|
17 | "dp.publish({'text/plain' : '5', 'image/png' : 5})" | |
18 | ); |
|
18 | ); | |
19 | cell.execute(); |
|
19 | cell.execute(); | |
20 | }); |
|
20 | }); |
@@ -1,20 +1,10 | |||||
1 | """Base classes to manage a Client's interaction with a running kernel |
|
1 | """Base classes to manage a Client's interaction with a running kernel""" | |
2 | """ |
|
|||
3 |
|
||||
4 | #----------------------------------------------------------------------------- |
|
|||
5 | # Copyright (C) 2013 The IPython Development Team |
|
|||
6 | # |
|
|||
7 | # Distributed under the terms of the BSD License. The full license is in |
|
|||
8 | # the file COPYING, distributed as part of this software. |
|
|||
9 | #----------------------------------------------------------------------------- |
|
|||
10 |
|
2 | |||
11 | #----------------------------------------------------------------------------- |
|
3 | # Copyright (c) IPython Development Team. | |
12 | # Imports |
|
4 | # Distributed under the terms of the Modified BSD License. | |
13 | #----------------------------------------------------------------------------- |
|
|||
14 |
|
5 | |||
15 | from __future__ import absolute_import |
|
6 | from __future__ import absolute_import | |
16 |
|
7 | |||
17 | # Standard library imports |
|
|||
18 | import atexit |
|
8 | import atexit | |
19 | import errno |
|
9 | import errno | |
20 | from threading import Thread |
|
10 | from threading import Thread | |
@@ -196,7 +186,7 class ShellChannel(ZMQSocketChannel): | |||||
196 | proxy_methods = [ |
|
186 | proxy_methods = [ | |
197 | 'execute', |
|
187 | 'execute', | |
198 | 'complete', |
|
188 | 'complete', | |
199 |
' |
|
189 | 'inspect', | |
200 | 'history', |
|
190 | 'history', | |
201 | 'kernel_info', |
|
191 | 'kernel_info', | |
202 | 'shutdown', |
|
192 | 'shutdown', | |
@@ -227,7 +217,7 class ShellChannel(ZMQSocketChannel): | |||||
227 | raise NotImplementedError('call_handlers must be defined in a subclass.') |
|
217 | raise NotImplementedError('call_handlers must be defined in a subclass.') | |
228 |
|
218 | |||
229 | def execute(self, code, silent=False, store_history=True, |
|
219 | def execute(self, code, silent=False, store_history=True, | |
230 |
|
|
220 | user_expressions=None, allow_stdin=None): | |
231 | """Execute code in the kernel. |
|
221 | """Execute code in the kernel. | |
232 |
|
222 | |||
233 | Parameters |
|
223 | Parameters | |
@@ -243,11 +233,6 class ShellChannel(ZMQSocketChannel): | |||||
243 | If set, the kernel will store command history. This is forced |
|
233 | If set, the kernel will store command history. This is forced | |
244 | to be False if silent is True. |
|
234 | to be False if silent is True. | |
245 |
|
235 | |||
246 | user_variables : list, optional |
|
|||
247 | A list of variable names to pull from the user's namespace. They |
|
|||
248 | will come back as a dict with these names as keys and their |
|
|||
249 | :func:`repr` as values. |
|
|||
250 |
|
||||
251 | user_expressions : dict, optional |
|
236 | user_expressions : dict, optional | |
252 | A dict mapping names to expressions to be evaluated in the user's |
|
237 | A dict mapping names to expressions to be evaluated in the user's | |
253 | dict. The expression values are returned as strings formatted using |
|
238 | dict. The expression values are returned as strings formatted using | |
@@ -264,8 +249,6 class ShellChannel(ZMQSocketChannel): | |||||
264 | ------- |
|
249 | ------- | |
265 | The msg_id of the message sent. |
|
250 | The msg_id of the message sent. | |
266 | """ |
|
251 | """ | |
267 | if user_variables is None: |
|
|||
268 | user_variables = [] |
|
|||
269 | if user_expressions is None: |
|
252 | if user_expressions is None: | |
270 | user_expressions = {} |
|
253 | user_expressions = {} | |
271 | if allow_stdin is None: |
|
254 | if allow_stdin is None: | |
@@ -275,13 +258,11 class ShellChannel(ZMQSocketChannel): | |||||
275 | # Don't waste network traffic if inputs are invalid |
|
258 | # Don't waste network traffic if inputs are invalid | |
276 | if not isinstance(code, string_types): |
|
259 | if not isinstance(code, string_types): | |
277 | raise ValueError('code %r must be a string' % code) |
|
260 | raise ValueError('code %r must be a string' % code) | |
278 | validate_string_list(user_variables) |
|
|||
279 | validate_string_dict(user_expressions) |
|
261 | validate_string_dict(user_expressions) | |
280 |
|
262 | |||
281 | # Create class for content/msg creation. Related to, but possibly |
|
263 | # Create class for content/msg creation. Related to, but possibly | |
282 | # not in Session. |
|
264 | # not in Session. | |
283 | content = dict(code=code, silent=silent, store_history=store_history, |
|
265 | content = dict(code=code, silent=silent, store_history=store_history, | |
284 | user_variables=user_variables, |
|
|||
285 | user_expressions=user_expressions, |
|
266 | user_expressions=user_expressions, | |
286 | allow_stdin=allow_stdin, |
|
267 | allow_stdin=allow_stdin, | |
287 | ) |
|
268 | ) | |
@@ -289,38 +270,42 class ShellChannel(ZMQSocketChannel): | |||||
289 | self._queue_send(msg) |
|
270 | self._queue_send(msg) | |
290 | return msg['header']['msg_id'] |
|
271 | return msg['header']['msg_id'] | |
291 |
|
272 | |||
292 |
def complete(self, |
|
273 | def complete(self, code, cursor_pos=None): | |
293 | """Tab complete text in the kernel's namespace. |
|
274 | """Tab complete text in the kernel's namespace. | |
294 |
|
275 | |||
295 | Parameters |
|
276 | Parameters | |
296 | ---------- |
|
277 | ---------- | |
297 |
|
|
278 | code : str | |
298 |
The |
|
279 | The context in which completion is requested. | |
299 | line : str |
|
280 | Can be anything between a variable name and an entire cell. | |
300 | The full line of text that is the surrounding context for the |
|
281 | cursor_pos : int, optional | |
301 | text to complete. |
|
282 | The position of the cursor in the block of code where the completion was requested. | |
302 | cursor_pos : int |
|
283 | Default: ``len(code)`` | |
303 | The position of the cursor in the line where the completion was |
|
|||
304 | requested. |
|
|||
305 | block : str, optional |
|
|||
306 | The full block of code in which the completion is being requested. |
|
|||
307 |
|
284 | |||
308 | Returns |
|
285 | Returns | |
309 | ------- |
|
286 | ------- | |
310 | The msg_id of the message sent. |
|
287 | The msg_id of the message sent. | |
311 | """ |
|
288 | """ | |
312 | content = dict(text=text, line=line, block=block, cursor_pos=cursor_pos) |
|
289 | if cursor_pos is None: | |
|
290 | cursor_pos = len(code) | |||
|
291 | content = dict(code=code, cursor_pos=cursor_pos) | |||
313 | msg = self.session.msg('complete_request', content) |
|
292 | msg = self.session.msg('complete_request', content) | |
314 | self._queue_send(msg) |
|
293 | self._queue_send(msg) | |
315 | return msg['header']['msg_id'] |
|
294 | return msg['header']['msg_id'] | |
316 |
|
295 | |||
317 |
def |
|
296 | def inspect(self, code, cursor_pos=None, detail_level=0): | |
318 | """Get metadata information about an object in the kernel's namespace. |
|
297 | """Get metadata information about an object in the kernel's namespace. | |
319 |
|
298 | |||
|
299 | It is up to the kernel to determine the appropriate object to inspect. | |||
|
300 | ||||
320 | Parameters |
|
301 | Parameters | |
321 | ---------- |
|
302 | ---------- | |
322 |
|
|
303 | code : str | |
323 | A string specifying the object name. |
|
304 | The context in which info is requested. | |
|
305 | Can be anything between a variable name and an entire cell. | |||
|
306 | cursor_pos : int, optional | |||
|
307 | The position of the cursor in the block of code where the info was requested. | |||
|
308 | Default: ``len(code)`` | |||
324 | detail_level : int, optional |
|
309 | detail_level : int, optional | |
325 | The level of detail for the introspection (0-2) |
|
310 | The level of detail for the introspection (0-2) | |
326 |
|
311 | |||
@@ -328,8 +313,12 class ShellChannel(ZMQSocketChannel): | |||||
328 | ------- |
|
313 | ------- | |
329 | The msg_id of the message sent. |
|
314 | The msg_id of the message sent. | |
330 | """ |
|
315 | """ | |
331 | content = dict(oname=oname, detail_level=detail_level) |
|
316 | if cursor_pos is None: | |
332 | msg = self.session.msg('object_info_request', content) |
|
317 | cursor_pos = len(code) | |
|
318 | content = dict(code=code, cursor_pos=cursor_pos, | |||
|
319 | detail_level=detail_level, | |||
|
320 | ) | |||
|
321 | msg = self.session.msg('inspect_request', content) | |||
333 | self._queue_send(msg) |
|
322 | self._queue_send(msg) | |
334 | return msg['header']['msg_id'] |
|
323 | return msg['header']['msg_id'] | |
335 |
|
324 |
@@ -1,11 +1,7 | |||||
1 | """Abstract base classes for kernel client channels""" |
|
1 | """Abstract base classes for kernel client channels""" | |
2 |
|
2 | |||
3 | #----------------------------------------------------------------------------- |
|
3 | # Copyright (c) IPython Development Team. | |
4 | # Copyright (C) 2013 The IPython Development Team |
|
4 | # Distributed under the terms of the Modified BSD License. | |
5 | # |
|
|||
6 | # Distributed under the terms of the BSD License. The full license is in |
|
|||
7 | # the file COPYING, distributed as part of this software. |
|
|||
8 | #----------------------------------------------------------------------------- |
|
|||
9 |
|
5 | |||
10 | import abc |
|
6 | import abc | |
11 |
|
7 | |||
@@ -42,7 +38,7 class ShellChannelABC(ChannelABC): | |||||
42 |
|
38 | |||
43 | @abc.abstractmethod |
|
39 | @abc.abstractmethod | |
44 | def execute(self, code, silent=False, store_history=True, |
|
40 | def execute(self, code, silent=False, store_history=True, | |
45 |
|
|
41 | user_expressions=None, allow_stdin=None): | |
46 | pass |
|
42 | pass | |
47 |
|
43 | |||
48 | @abc.abstractmethod |
|
44 | @abc.abstractmethod | |
@@ -50,7 +46,7 class ShellChannelABC(ChannelABC): | |||||
50 | pass |
|
46 | pass | |
51 |
|
47 | |||
52 | @abc.abstractmethod |
|
48 | @abc.abstractmethod | |
53 |
def |
|
49 | def inspect(self, oname, detail_level=0): | |
54 | pass |
|
50 | pass | |
55 |
|
51 | |||
56 | @abc.abstractmethod |
|
52 | @abc.abstractmethod |
@@ -1,23 +1,13 | |||||
1 |
""" |
|
1 | """A kernel client for in-process kernels.""" | |
2 |
|
2 | |||
3 | #----------------------------------------------------------------------------- |
|
3 | # Copyright (c) IPython Development Team. | |
4 | # Copyright (C) 2012 The IPython Development Team |
|
4 | # Distributed under the terms of the Modified BSD License. | |
5 | # |
|
|||
6 | # Distributed under the terms of the BSD License. The full license is in |
|
|||
7 | # the file COPYING, distributed as part of this software. |
|
|||
8 | #----------------------------------------------------------------------------- |
|
|||
9 |
|
||||
10 | #----------------------------------------------------------------------------- |
|
|||
11 | # Imports |
|
|||
12 | #----------------------------------------------------------------------------- |
|
|||
13 |
|
5 | |||
14 | # IPython imports |
|
|||
15 | from IPython.kernel.channelsabc import ( |
|
6 | from IPython.kernel.channelsabc import ( | |
16 | ShellChannelABC, IOPubChannelABC, |
|
7 | ShellChannelABC, IOPubChannelABC, | |
17 | HBChannelABC, StdInChannelABC, |
|
8 | HBChannelABC, StdInChannelABC, | |
18 | ) |
|
9 | ) | |
19 |
|
10 | |||
20 | # Local imports |
|
|||
21 | from .socket import DummySocket |
|
11 | from .socket import DummySocket | |
22 |
|
12 | |||
23 | #----------------------------------------------------------------------------- |
|
13 | #----------------------------------------------------------------------------- | |
@@ -83,7 +73,7 class InProcessShellChannel(InProcessChannel): | |||||
83 | proxy_methods = [ |
|
73 | proxy_methods = [ | |
84 | 'execute', |
|
74 | 'execute', | |
85 | 'complete', |
|
75 | 'complete', | |
86 |
' |
|
76 | 'inspect', | |
87 | 'history', |
|
77 | 'history', | |
88 | 'shutdown', |
|
78 | 'shutdown', | |
89 | 'kernel_info', |
|
79 | 'kernel_info', | |
@@ -94,26 +84,31 class InProcessShellChannel(InProcessChannel): | |||||
94 | #-------------------------------------------------------------------------- |
|
84 | #-------------------------------------------------------------------------- | |
95 |
|
85 | |||
96 | def execute(self, code, silent=False, store_history=True, |
|
86 | def execute(self, code, silent=False, store_history=True, | |
97 |
|
|
87 | user_expressions={}, allow_stdin=None): | |
98 | if allow_stdin is None: |
|
88 | if allow_stdin is None: | |
99 | allow_stdin = self.allow_stdin |
|
89 | allow_stdin = self.allow_stdin | |
100 | content = dict(code=code, silent=silent, store_history=store_history, |
|
90 | content = dict(code=code, silent=silent, store_history=store_history, | |
101 | user_variables=user_variables, |
|
|||
102 | user_expressions=user_expressions, |
|
91 | user_expressions=user_expressions, | |
103 | allow_stdin=allow_stdin) |
|
92 | allow_stdin=allow_stdin) | |
104 | msg = self.client.session.msg('execute_request', content) |
|
93 | msg = self.client.session.msg('execute_request', content) | |
105 | self._dispatch_to_kernel(msg) |
|
94 | self._dispatch_to_kernel(msg) | |
106 | return msg['header']['msg_id'] |
|
95 | return msg['header']['msg_id'] | |
107 |
|
96 | |||
108 |
def complete(self, |
|
97 | def complete(self, code, cursor_pos=None): | |
109 | content = dict(text=text, line=line, block=block, cursor_pos=cursor_pos) |
|
98 | if cursor_pos is None: | |
|
99 | cursor_pos = len(code) | |||
|
100 | content = dict(code=code, cursor_pos=cursor_pos) | |||
110 | msg = self.client.session.msg('complete_request', content) |
|
101 | msg = self.client.session.msg('complete_request', content) | |
111 | self._dispatch_to_kernel(msg) |
|
102 | self._dispatch_to_kernel(msg) | |
112 | return msg['header']['msg_id'] |
|
103 | return msg['header']['msg_id'] | |
113 |
|
104 | |||
114 |
def |
|
105 | def inspect(self, code, cursor_pos=None, detail_level=0): | |
115 | content = dict(oname=oname, detail_level=detail_level) |
|
106 | if cursor_pos is None: | |
116 | msg = self.client.session.msg('object_info_request', content) |
|
107 | cursor_pos = len(code) | |
|
108 | content = dict(code=code, cursor_pos=cursor_pos, | |||
|
109 | detail_level=detail_level, | |||
|
110 | ) | |||
|
111 | msg = self.client.session.msg('inspect_request', content) | |||
117 | self._dispatch_to_kernel(msg) |
|
112 | self._dispatch_to_kernel(msg) | |
118 | return msg['header']['msg_id'] |
|
113 | return msg['header']['msg_id'] | |
119 |
|
114 |
@@ -1,22 +1,12 | |||||
1 | """An in-process kernel""" |
|
1 | """An in-process kernel""" | |
2 |
|
2 | |||
3 | #----------------------------------------------------------------------------- |
|
3 | # Copyright (c) IPython Development Team. | |
4 | # Copyright (C) 2012 The IPython Development Team |
|
4 | # Distributed under the terms of the Modified BSD License. | |
5 | # |
|
|||
6 | # Distributed under the terms of the BSD License. The full license is in |
|
|||
7 | # the file COPYING, distributed as part of this software. |
|
|||
8 | #----------------------------------------------------------------------------- |
|
|||
9 |
|
||||
10 | #----------------------------------------------------------------------------- |
|
|||
11 | # Imports |
|
|||
12 | #----------------------------------------------------------------------------- |
|
|||
13 |
|
5 | |||
14 | # Standard library imports |
|
|||
15 | from contextlib import contextmanager |
|
6 | from contextlib import contextmanager | |
16 | import logging |
|
7 | import logging | |
17 | import sys |
|
8 | import sys | |
18 |
|
9 | |||
19 | # Local imports |
|
|||
20 | from IPython.core.interactiveshell import InteractiveShellABC |
|
10 | from IPython.core.interactiveshell import InteractiveShellABC | |
21 | from IPython.utils.jsonutil import json_clean |
|
11 | from IPython.utils.jsonutil import json_clean | |
22 | from IPython.utils.traitlets import Any, Enum, Instance, List, Type |
|
12 | from IPython.utils.traitlets import Any, Enum, Instance, List, Type | |
@@ -83,14 +73,14 class InProcessKernel(Kernel): | |||||
83 | """ The in-process kernel doesn't abort requests. """ |
|
73 | """ The in-process kernel doesn't abort requests. """ | |
84 | pass |
|
74 | pass | |
85 |
|
75 | |||
86 |
def |
|
76 | def _input_request(self, prompt, ident, parent, password=False): | |
87 | # Flush output before making the request. |
|
77 | # Flush output before making the request. | |
88 | self.raw_input_str = None |
|
78 | self.raw_input_str = None | |
89 | sys.stderr.flush() |
|
79 | sys.stderr.flush() | |
90 | sys.stdout.flush() |
|
80 | sys.stdout.flush() | |
91 |
|
81 | |||
92 | # Send the input request. |
|
82 | # Send the input request. | |
93 | content = json_clean(dict(prompt=prompt)) |
|
83 | content = json_clean(dict(prompt=prompt, password=password)) | |
94 | msg = self.session.msg(u'input_request', content, parent) |
|
84 | msg = self.session.msg(u'input_request', content, parent) | |
95 | for frontend in self.frontends: |
|
85 | for frontend in self.frontends: | |
96 | if frontend.session.session == parent['header']['session']: |
|
86 | if frontend.session.session == parent['header']['session']: |
@@ -1,19 +1,10 | |||||
1 | #------------------------------------------------------------------------------- |
|
1 | # Copyright (c) IPython Development Team. | |
2 | # Copyright (C) 2012 The IPython Development Team |
|
2 | # Distributed under the terms of the Modified BSD License. | |
3 | # |
|
|||
4 | # Distributed under the terms of the BSD License. The full license is in |
|
|||
5 | # the file COPYING, distributed as part of this software. |
|
|||
6 | #------------------------------------------------------------------------------- |
|
|||
7 |
|
3 | |||
8 | #----------------------------------------------------------------------------- |
|
|||
9 | # Imports |
|
|||
10 | #----------------------------------------------------------------------------- |
|
|||
11 | from __future__ import print_function |
|
4 | from __future__ import print_function | |
12 |
|
5 | |||
13 | # Standard library imports |
|
|||
14 | import unittest |
|
6 | import unittest | |
15 |
|
7 | |||
16 | # Local imports |
|
|||
17 | from IPython.kernel.inprocess.blocking import BlockingInProcessKernelClient |
|
8 | from IPython.kernel.inprocess.blocking import BlockingInProcessKernelClient | |
18 | from IPython.kernel.inprocess.manager import InProcessKernelManager |
|
9 | from IPython.kernel.inprocess.manager import InProcessKernelManager | |
19 |
|
10 | |||
@@ -71,13 +62,13 class InProcessKernelManagerTestCase(unittest.TestCase): | |||||
71 | kc = BlockingInProcessKernelClient(kernel=km.kernel) |
|
62 | kc = BlockingInProcessKernelClient(kernel=km.kernel) | |
72 | kc.start_channels() |
|
63 | kc.start_channels() | |
73 | km.kernel.shell.push({'my_bar': 0, 'my_baz': 1}) |
|
64 | km.kernel.shell.push({'my_bar': 0, 'my_baz': 1}) | |
74 |
kc.complete('my_ba', |
|
65 | kc.complete('my_ba', 5) | |
75 | msg = kc.get_shell_msg() |
|
66 | msg = kc.get_shell_msg() | |
76 | self.assertEqual(msg['header']['msg_type'], 'complete_reply') |
|
67 | self.assertEqual(msg['header']['msg_type'], 'complete_reply') | |
77 | self.assertEqual(sorted(msg['content']['matches']), |
|
68 | self.assertEqual(sorted(msg['content']['matches']), | |
78 | ['my_bar', 'my_baz']) |
|
69 | ['my_bar', 'my_baz']) | |
79 |
|
70 | |||
80 |
def test_ |
|
71 | def test_inspect(self): | |
81 | """ Does requesting object information from an in-process kernel work? |
|
72 | """ Does requesting object information from an in-process kernel work? | |
82 | """ |
|
73 | """ | |
83 | km = InProcessKernelManager() |
|
74 | km = InProcessKernelManager() | |
@@ -85,11 +76,13 class InProcessKernelManagerTestCase(unittest.TestCase): | |||||
85 | kc = BlockingInProcessKernelClient(kernel=km.kernel) |
|
76 | kc = BlockingInProcessKernelClient(kernel=km.kernel) | |
86 | kc.start_channels() |
|
77 | kc.start_channels() | |
87 | km.kernel.shell.user_ns['foo'] = 1 |
|
78 | km.kernel.shell.user_ns['foo'] = 1 | |
88 |
kc. |
|
79 | kc.inspect('foo') | |
89 | msg = kc.get_shell_msg() |
|
80 | msg = kc.get_shell_msg() | |
90 |
self.assertEqual |
|
81 | self.assertEqual(msg['header']['msg_type'], 'inspect_reply') | |
91 | self.assertEquals(msg['content']['name'], 'foo') |
|
82 | content = msg['content'] | |
92 | self.assertEquals(msg['content']['type_name'], 'int') |
|
83 | assert content['found'] | |
|
84 | text = content['data']['text/plain'] | |||
|
85 | self.assertIn('int', text) | |||
93 |
|
86 | |||
94 | def test_history(self): |
|
87 | def test_history(self): | |
95 | """ Does requesting history from an in-process kernel work? |
|
88 | """ Does requesting history from an in-process kernel work? |
@@ -1,11 +1,7 | |||||
1 | """Abstract base class for kernel managers.""" |
|
1 | """Abstract base class for kernel managers.""" | |
2 |
|
2 | |||
3 | #----------------------------------------------------------------------------- |
|
3 | # Copyright (c) IPython Development Team. | |
4 | # Copyright (C) 2013 The IPython Development Team |
|
4 | # Distributed under the terms of the Modified BSD License. | |
5 | # |
|
|||
6 | # Distributed under the terms of the BSD License. The full license is in |
|
|||
7 | # the file COPYING, distributed as part of this software. |
|
|||
8 | #----------------------------------------------------------------------------- |
|
|||
9 |
|
5 | |||
10 | import abc |
|
6 | import abc | |
11 |
|
7 | |||
@@ -24,54 +20,6 class KernelManagerABC(with_metaclass(abc.ABCMeta, object)): | |||||
24 | def kernel(self): |
|
20 | def kernel(self): | |
25 | pass |
|
21 | pass | |
26 |
|
22 | |||
27 | @abc.abstractproperty |
|
|||
28 | def shell_channel_class(self): |
|
|||
29 | pass |
|
|||
30 |
|
||||
31 | @abc.abstractproperty |
|
|||
32 | def iopub_channel_class(self): |
|
|||
33 | pass |
|
|||
34 |
|
||||
35 | @abc.abstractproperty |
|
|||
36 | def hb_channel_class(self): |
|
|||
37 | pass |
|
|||
38 |
|
||||
39 | @abc.abstractproperty |
|
|||
40 | def stdin_channel_class(self): |
|
|||
41 | pass |
|
|||
42 |
|
||||
43 | #-------------------------------------------------------------------------- |
|
|||
44 | # Channel management methods |
|
|||
45 | #-------------------------------------------------------------------------- |
|
|||
46 |
|
||||
47 | @abc.abstractmethod |
|
|||
48 | def start_channels(self, shell=True, iopub=True, stdin=True, hb=True): |
|
|||
49 | pass |
|
|||
50 |
|
||||
51 | @abc.abstractmethod |
|
|||
52 | def stop_channels(self): |
|
|||
53 | pass |
|
|||
54 |
|
||||
55 | @abc.abstractproperty |
|
|||
56 | def channels_running(self): |
|
|||
57 | pass |
|
|||
58 |
|
||||
59 | @abc.abstractproperty |
|
|||
60 | def shell_channel(self): |
|
|||
61 | pass |
|
|||
62 |
|
||||
63 | @abc.abstractproperty |
|
|||
64 | def iopub_channel(self): |
|
|||
65 | pass |
|
|||
66 |
|
||||
67 | @abc.abstractproperty |
|
|||
68 | def stdin_channel(self): |
|
|||
69 | pass |
|
|||
70 |
|
||||
71 | @abc.abstractproperty |
|
|||
72 | def hb_channel(self): |
|
|||
73 | pass |
|
|||
74 |
|
||||
75 | #-------------------------------------------------------------------------- |
|
23 | #-------------------------------------------------------------------------- | |
76 | # Kernel management |
|
24 | # Kernel management | |
77 | #-------------------------------------------------------------------------- |
|
25 | #-------------------------------------------------------------------------- |
@@ -1,13 +1,10 | |||||
1 | """Test suite for our zeromq-based message specification. |
|
1 | """Test suite for our zeromq-based message specification.""" | |
2 | """ |
|
2 | ||
3 | #----------------------------------------------------------------------------- |
|
3 | # Copyright (c) IPython Development Team. | |
4 | # Copyright (C) 2010 The IPython Development Team |
|
4 | # Distributed under the terms of the Modified BSD License. | |
5 | # |
|
|||
6 | # Distributed under the terms of the BSD License. The full license is in |
|
|||
7 | # the file COPYING.txt, distributed as part of this software. |
|
|||
8 | #----------------------------------------------------------------------------- |
|
|||
9 |
|
5 | |||
10 | import re |
|
6 | import re | |
|
7 | from distutils.version import LooseVersion as V | |||
11 | from subprocess import PIPE |
|
8 | from subprocess import PIPE | |
12 | try: |
|
9 | try: | |
13 | from queue import Empty # Py 3 |
|
10 | from queue import Empty # Py 3 | |
@@ -60,7 +57,21 class Reference(HasTraits): | |||||
60 | try: |
|
57 | try: | |
61 | setattr(self, key, d[key]) |
|
58 | setattr(self, key, d[key]) | |
62 | except TraitError as e: |
|
59 | except TraitError as e: | |
63 |
|
|
60 | assert False, str(e) | |
|
61 | ||||
|
62 | ||||
|
63 | class Version(Unicode): | |||
|
64 | def __init__(self, *args, **kwargs): | |||
|
65 | self.min = kwargs.pop('min', None) | |||
|
66 | self.max = kwargs.pop('max', None) | |||
|
67 | kwargs['default_value'] = self.min | |||
|
68 | super(Version, self).__init__(*args, **kwargs) | |||
|
69 | ||||
|
70 | def validate(self, obj, value): | |||
|
71 | if self.min and V(value) < V(self.min): | |||
|
72 | raise TraitError("bad version: %s < %s" % (value, self.min)) | |||
|
73 | if self.max and (V(value) > V(self.max)): | |||
|
74 | raise TraitError("bad version: %s > %s" % (value, self.max)) | |||
64 |
|
75 | |||
65 |
|
76 | |||
66 | class RMessage(Reference): |
|
77 | class RMessage(Reference): | |
@@ -70,15 +81,30 class RMessage(Reference): | |||||
70 | parent_header = Dict() |
|
81 | parent_header = Dict() | |
71 | content = Dict() |
|
82 | content = Dict() | |
72 |
|
83 | |||
|
84 | def check(self, d): | |||
|
85 | super(RMessage, self).check(d) | |||
|
86 | RHeader().check(self.header) | |||
|
87 | if self.parent_header: | |||
|
88 | RHeader().check(self.parent_header) | |||
|
89 | ||||
73 | class RHeader(Reference): |
|
90 | class RHeader(Reference): | |
74 | msg_id = Unicode() |
|
91 | msg_id = Unicode() | |
75 | msg_type = Unicode() |
|
92 | msg_type = Unicode() | |
76 | session = Unicode() |
|
93 | session = Unicode() | |
77 | username = Unicode() |
|
94 | username = Unicode() | |
|
95 | version = Version(min='5.0') | |||
78 |
|
96 | |||
79 | class RContent(Reference): |
|
97 | mime_pat = re.compile(r'^[\w\-\+\.]+/[\w\-\+\.]+$') | |
80 | status = Enum((u'ok', u'error')) |
|
98 | ||
|
99 | class MimeBundle(Reference): | |||
|
100 | metadata = Dict() | |||
|
101 | data = Dict() | |||
|
102 | def _data_changed(self, name, old, new): | |||
|
103 | for k,v in iteritems(new): | |||
|
104 | assert mime_pat.match(k) | |||
|
105 | nt.assert_is_instance(v, string_types) | |||
81 |
|
106 | |||
|
107 | # shell replies | |||
82 |
|
108 | |||
83 | class ExecuteReply(Reference): |
|
109 | class ExecuteReply(Reference): | |
84 | execution_count = Integer() |
|
110 | execution_count = Integer() | |
@@ -94,7 +120,6 class ExecuteReply(Reference): | |||||
94 |
|
120 | |||
95 | class ExecuteReplyOkay(Reference): |
|
121 | class ExecuteReplyOkay(Reference): | |
96 | payload = List(Dict) |
|
122 | payload = List(Dict) | |
97 | user_variables = Dict() |
|
|||
98 | user_expressions = Dict() |
|
123 | user_expressions = Dict() | |
99 |
|
124 | |||
100 |
|
125 | |||
@@ -104,31 +129,8 class ExecuteReplyError(Reference): | |||||
104 | traceback = List(Unicode) |
|
129 | traceback = List(Unicode) | |
105 |
|
130 | |||
106 |
|
131 | |||
107 |
class |
|
132 | class InspectReply(MimeBundle): | |
108 | name = Unicode() |
|
|||
109 | found = Bool() |
|
133 | found = Bool() | |
110 | ismagic = Bool() |
|
|||
111 | isalias = Bool() |
|
|||
112 | namespace = Enum((u'builtin', u'magics', u'alias', u'Interactive')) |
|
|||
113 | type_name = Unicode() |
|
|||
114 | string_form = Unicode() |
|
|||
115 | base_class = Unicode() |
|
|||
116 | length = Integer() |
|
|||
117 | file = Unicode() |
|
|||
118 | definition = Unicode() |
|
|||
119 | argspec = Dict() |
|
|||
120 | init_definition = Unicode() |
|
|||
121 | docstring = Unicode() |
|
|||
122 | init_docstring = Unicode() |
|
|||
123 | class_docstring = Unicode() |
|
|||
124 | call_def = Unicode() |
|
|||
125 | call_docstring = Unicode() |
|
|||
126 | source = Unicode() |
|
|||
127 |
|
||||
128 | def check(self, d): |
|
|||
129 | Reference.check(self, d) |
|
|||
130 | if d['argspec'] is not None: |
|
|||
131 | ArgSpec().check(d['argspec']) |
|
|||
132 |
|
134 | |||
133 |
|
135 | |||
134 | class ArgSpec(Reference): |
|
136 | class ArgSpec(Reference): | |
@@ -144,33 +146,28 class Status(Reference): | |||||
144 |
|
146 | |||
145 | class CompleteReply(Reference): |
|
147 | class CompleteReply(Reference): | |
146 | matches = List(Unicode) |
|
148 | matches = List(Unicode) | |
147 |
|
149 | cursor_start = Integer() | ||
148 |
|
150 | cursor_end = Integer() | ||
149 | def Version(num, trait=Integer): |
|
151 | status = Unicode() | |
150 | return List(trait, default_value=[0] * num, minlen=num, maxlen=num) |
|
|||
151 |
|
152 | |||
152 |
|
153 | |||
153 | class KernelInfoReply(Reference): |
|
154 | class KernelInfoReply(Reference): | |
154 |
|
155 | protocol_version = Version(min='5.0') | ||
155 | protocol_version = Version(2) |
|
156 | implementation = Unicode('ipython') | |
156 |
i |
|
157 | implementation_version = Version(min='2.1') | |
157 |
language_version = Version( |
|
158 | language_version = Version(min='2.7') | |
158 | language = Unicode() |
|
159 | language = Unicode('python') | |
159 |
|
160 | banner = Unicode() | ||
160 | def _ipython_version_changed(self, name, old, new): |
|
|||
161 | for v in new: |
|
|||
162 | assert isinstance(v, int) or isinstance(v, string_types), \ |
|
|||
163 | 'expected int or string as version component, got {0!r}'.format(v) |
|
|||
164 |
|
161 | |||
165 |
|
162 | |||
166 | # IOPub messages |
|
163 | # IOPub messages | |
167 |
|
164 | |||
168 |
class |
|
165 | class ExecuteInput(Reference): | |
169 | code = Unicode() |
|
166 | code = Unicode() | |
170 | execution_count = Integer() |
|
167 | execution_count = Integer() | |
171 |
|
168 | |||
172 |
|
169 | |||
173 |
|
|
170 | Error = ExecuteReplyError | |
174 |
|
171 | |||
175 |
|
172 | |||
176 | class Stream(Reference): |
|
173 | class Stream(Reference): | |
@@ -178,38 +175,26 class Stream(Reference): | |||||
178 | data = Unicode() |
|
175 | data = Unicode() | |
179 |
|
176 | |||
180 |
|
177 | |||
181 | mime_pat = re.compile(r'\w+/\w+') |
|
178 | class DisplayData(MimeBundle): | |
|
179 | pass | |||
182 |
|
180 | |||
183 | class DisplayData(Reference): |
|
|||
184 | source = Unicode() |
|
|||
185 | metadata = Dict() |
|
|||
186 | data = Dict() |
|
|||
187 | def _data_changed(self, name, old, new): |
|
|||
188 | for k,v in iteritems(new): |
|
|||
189 | assert mime_pat.match(k) |
|
|||
190 | nt.assert_is_instance(v, string_types) |
|
|||
191 |
|
181 | |||
192 |
|
182 | class ExecuteResult(MimeBundle): | ||
193 | class PyOut(Reference): |
|
|||
194 | execution_count = Integer() |
|
183 | execution_count = Integer() | |
195 | data = Dict() |
|
|||
196 | def _data_changed(self, name, old, new): |
|
|||
197 | for k,v in iteritems(new): |
|
|||
198 | assert mime_pat.match(k) |
|
|||
199 | nt.assert_is_instance(v, string_types) |
|
|||
200 |
|
184 | |||
201 |
|
185 | |||
202 | references = { |
|
186 | references = { | |
203 | 'execute_reply' : ExecuteReply(), |
|
187 | 'execute_reply' : ExecuteReply(), | |
204 |
' |
|
188 | 'inspect_reply' : InspectReply(), | |
205 | 'status' : Status(), |
|
189 | 'status' : Status(), | |
206 | 'complete_reply' : CompleteReply(), |
|
190 | 'complete_reply' : CompleteReply(), | |
207 | 'kernel_info_reply': KernelInfoReply(), |
|
191 | 'kernel_info_reply': KernelInfoReply(), | |
208 | 'pyin' : PyIn(), |
|
192 | 'execute_input' : ExecuteInput(), | |
209 | 'pyout' : PyOut(), |
|
193 | 'execute_result' : ExecuteResult(), | |
210 |
' |
|
194 | 'error' : Error(), | |
211 | 'stream' : Stream(), |
|
195 | 'stream' : Stream(), | |
212 | 'display_data' : DisplayData(), |
|
196 | 'display_data' : DisplayData(), | |
|
197 | 'header' : RHeader(), | |||
213 | } |
|
198 | } | |
214 | """ |
|
199 | """ | |
215 | Specifications of `content` part of the reply messages. |
|
200 | Specifications of `content` part of the reply messages. | |
@@ -280,8 +265,8 def test_execute_error(): | |||||
280 | nt.assert_equal(reply['status'], 'error') |
|
265 | nt.assert_equal(reply['status'], 'error') | |
281 | nt.assert_equal(reply['ename'], 'ZeroDivisionError') |
|
266 | nt.assert_equal(reply['ename'], 'ZeroDivisionError') | |
282 |
|
267 | |||
283 |
|
|
268 | error = KC.iopub_channel.get_msg(timeout=TIMEOUT) | |
284 |
validate_message( |
|
269 | validate_message(error, 'error', msg_id) | |
285 |
|
270 | |||
286 |
|
271 | |||
287 | def test_execute_inc(): |
|
272 | def test_execute_inc(): | |
@@ -298,28 +283,6 def test_execute_inc(): | |||||
298 | nt.assert_equal(count_2, count+1) |
|
283 | nt.assert_equal(count_2, count+1) | |
299 |
|
284 | |||
300 |
|
285 | |||
301 | def test_user_variables(): |
|
|||
302 | flush_channels() |
|
|||
303 |
|
||||
304 | msg_id, reply = execute(code='x=1', user_variables=['x']) |
|
|||
305 | user_variables = reply['user_variables'] |
|
|||
306 | nt.assert_equal(user_variables, {u'x': { |
|
|||
307 | u'status': u'ok', |
|
|||
308 | u'data': {u'text/plain': u'1'}, |
|
|||
309 | u'metadata': {}, |
|
|||
310 | }}) |
|
|||
311 |
|
||||
312 |
|
||||
313 | def test_user_variables_fail(): |
|
|||
314 | flush_channels() |
|
|||
315 |
|
||||
316 | msg_id, reply = execute(code='x=1', user_variables=['nosuchname']) |
|
|||
317 | user_variables = reply['user_variables'] |
|
|||
318 | foo = user_variables['nosuchname'] |
|
|||
319 | nt.assert_equal(foo['status'], 'error') |
|
|||
320 | nt.assert_equal(foo['ename'], 'KeyError') |
|
|||
321 |
|
||||
322 |
|
||||
323 | def test_user_expressions(): |
|
286 | def test_user_expressions(): | |
324 | flush_channels() |
|
287 | flush_channels() | |
325 |
|
288 | |||
@@ -345,9 +308,9 def test_user_expressions_fail(): | |||||
345 | def test_oinfo(): |
|
308 | def test_oinfo(): | |
346 | flush_channels() |
|
309 | flush_channels() | |
347 |
|
310 | |||
348 |
msg_id = KC. |
|
311 | msg_id = KC.inspect('a') | |
349 | reply = KC.get_shell_msg(timeout=TIMEOUT) |
|
312 | reply = KC.get_shell_msg(timeout=TIMEOUT) | |
350 |
validate_message(reply, ' |
|
313 | validate_message(reply, 'inspect_reply', msg_id) | |
351 |
|
314 | |||
352 |
|
315 | |||
353 | def test_oinfo_found(): |
|
316 | def test_oinfo_found(): | |
@@ -355,13 +318,14 def test_oinfo_found(): | |||||
355 |
|
318 | |||
356 | msg_id, reply = execute(code='a=5') |
|
319 | msg_id, reply = execute(code='a=5') | |
357 |
|
320 | |||
358 |
msg_id = KC. |
|
321 | msg_id = KC.inspect('a') | |
359 | reply = KC.get_shell_msg(timeout=TIMEOUT) |
|
322 | reply = KC.get_shell_msg(timeout=TIMEOUT) | |
360 |
validate_message(reply, ' |
|
323 | validate_message(reply, 'inspect_reply', msg_id) | |
361 | content = reply['content'] |
|
324 | content = reply['content'] | |
362 | assert content['found'] |
|
325 | assert content['found'] | |
363 | argspec = content['argspec'] |
|
326 | text = content['data']['text/plain'] | |
364 |
nt.assert_i |
|
327 | nt.assert_in('Type:', text) | |
|
328 | nt.assert_in('Docstring:', text) | |||
365 |
|
329 | |||
366 |
|
330 | |||
367 | def test_oinfo_detail(): |
|
331 | def test_oinfo_detail(): | |
@@ -369,22 +333,22 def test_oinfo_detail(): | |||||
369 |
|
333 | |||
370 | msg_id, reply = execute(code='ip=get_ipython()') |
|
334 | msg_id, reply = execute(code='ip=get_ipython()') | |
371 |
|
335 | |||
372 |
msg_id = KC. |
|
336 | msg_id = KC.inspect('ip.object_inspect', cursor_pos=10, detail_level=1) | |
373 | reply = KC.get_shell_msg(timeout=TIMEOUT) |
|
337 | reply = KC.get_shell_msg(timeout=TIMEOUT) | |
374 |
validate_message(reply, ' |
|
338 | validate_message(reply, 'inspect_reply', msg_id) | |
375 | content = reply['content'] |
|
339 | content = reply['content'] | |
376 | assert content['found'] |
|
340 | assert content['found'] | |
377 | argspec = content['argspec'] |
|
341 | text = content['data']['text/plain'] | |
378 | nt.assert_is_instance(argspec, dict, "expected non-empty argspec dict, got %r" % argspec) |
|
342 | nt.assert_in('Definition:', text) | |
379 | nt.assert_equal(argspec['defaults'], [0]) |
|
343 | nt.assert_in('Source:', text) | |
380 |
|
344 | |||
381 |
|
345 | |||
382 | def test_oinfo_not_found(): |
|
346 | def test_oinfo_not_found(): | |
383 | flush_channels() |
|
347 | flush_channels() | |
384 |
|
348 | |||
385 |
msg_id = KC. |
|
349 | msg_id = KC.inspect('dne') | |
386 | reply = KC.get_shell_msg(timeout=TIMEOUT) |
|
350 | reply = KC.get_shell_msg(timeout=TIMEOUT) | |
387 |
validate_message(reply, ' |
|
351 | validate_message(reply, 'inspect_reply', msg_id) | |
388 | content = reply['content'] |
|
352 | content = reply['content'] | |
389 | nt.assert_false(content['found']) |
|
353 | nt.assert_false(content['found']) | |
390 |
|
354 | |||
@@ -394,7 +358,7 def test_complete(): | |||||
394 |
|
358 | |||
395 | msg_id, reply = execute(code="alpha = albert = 5") |
|
359 | msg_id, reply = execute(code="alpha = albert = 5") | |
396 |
|
360 | |||
397 |
msg_id = KC.complete('al', |
|
361 | msg_id = KC.complete('al', 2) | |
398 | reply = KC.get_shell_msg(timeout=TIMEOUT) |
|
362 | reply = KC.get_shell_msg(timeout=TIMEOUT) | |
399 | validate_message(reply, 'complete_reply', msg_id) |
|
363 | validate_message(reply, 'complete_reply', msg_id) | |
400 | matches = reply['content']['matches'] |
|
364 | matches = reply['content']['matches'] | |
@@ -430,7 +394,6 def test_stream(): | |||||
430 | stdout = KC.iopub_channel.get_msg(timeout=TIMEOUT) |
|
394 | stdout = KC.iopub_channel.get_msg(timeout=TIMEOUT) | |
431 | validate_message(stdout, 'stream', msg_id) |
|
395 | validate_message(stdout, 'stream', msg_id) | |
432 | content = stdout['content'] |
|
396 | content = stdout['content'] | |
433 | nt.assert_equal(content['name'], u'stdout') |
|
|||
434 | nt.assert_equal(content['data'], u'hi\n') |
|
397 | nt.assert_equal(content['data'], u'hi\n') | |
435 |
|
398 | |||
436 |
|
399 |
@@ -1,15 +1,7 | |||||
1 | """utilities for testing IPython kernels""" |
|
1 | """utilities for testing IPython kernels""" | |
2 |
|
2 | |||
3 | #------------------------------------------------------------------------------- |
|
3 | # Copyright (c) IPython Development Team. | |
4 | # Copyright (C) 2013 The IPython Development Team |
|
4 | # Distributed under the terms of the Modified BSD License. | |
5 | # |
|
|||
6 | # Distributed under the terms of the BSD License. The full license is in |
|
|||
7 | # the file COPYING, distributed as part of this software. |
|
|||
8 | #------------------------------------------------------------------------------- |
|
|||
9 |
|
||||
10 | #------------------------------------------------------------------------------- |
|
|||
11 | # Imports |
|
|||
12 | #------------------------------------------------------------------------------- |
|
|||
13 |
|
5 | |||
14 | import atexit |
|
6 | import atexit | |
15 |
|
7 | |||
@@ -84,9 +76,9 def execute(code='', kc=None, **kwargs): | |||||
84 | nt.assert_equal(busy['content']['execution_state'], 'busy') |
|
76 | nt.assert_equal(busy['content']['execution_state'], 'busy') | |
85 |
|
77 | |||
86 | if not kwargs.get('silent'): |
|
78 | if not kwargs.get('silent'): | |
87 |
|
|
79 | execute_input = kc.get_iopub_msg(timeout=TIMEOUT) | |
88 |
validate_message( |
|
80 | validate_message(execute_input, 'execute_input', msg_id) | |
89 |
nt.assert_equal( |
|
81 | nt.assert_equal(execute_input['content']['code'], code) | |
90 |
|
82 | |||
91 | return msg_id, reply['content'] |
|
83 | return msg_id, reply['content'] | |
92 |
|
84 |
@@ -1,5 +1,8 | |||||
1 | """Replacements for sys.displayhook that publish over ZMQ. |
|
1 | """Replacements for sys.displayhook that publish over ZMQ.""" | |
2 | """ |
|
2 | ||
|
3 | # Copyright (c) IPython Development Team. | |||
|
4 | # Distributed under the terms of the Modified BSD License. | |||
|
5 | ||||
3 | import sys |
|
6 | import sys | |
4 |
|
7 | |||
5 | from IPython.core.displayhook import DisplayHook |
|
8 | from IPython.core.displayhook import DisplayHook | |
@@ -12,7 +15,7 from .session import extract_header, Session | |||||
12 | class ZMQDisplayHook(object): |
|
15 | class ZMQDisplayHook(object): | |
13 | """A simple displayhook that publishes the object's repr over a ZeroMQ |
|
16 | """A simple displayhook that publishes the object's repr over a ZeroMQ | |
14 | socket.""" |
|
17 | socket.""" | |
15 |
topic=b' |
|
18 | topic=b'execute_result' | |
16 |
|
19 | |||
17 | def __init__(self, session, pub_socket): |
|
20 | def __init__(self, session, pub_socket): | |
18 | self.session = session |
|
21 | self.session = session | |
@@ -26,7 +29,7 class ZMQDisplayHook(object): | |||||
26 | builtin_mod._ = obj |
|
29 | builtin_mod._ = obj | |
27 | sys.stdout.flush() |
|
30 | sys.stdout.flush() | |
28 | sys.stderr.flush() |
|
31 | sys.stderr.flush() | |
29 |
msg = self.session.send(self.pub_socket, u' |
|
32 | msg = self.session.send(self.pub_socket, u'execute_result', {u'data':repr(obj)}, | |
30 | parent=self.parent_header, ident=self.topic) |
|
33 | parent=self.parent_header, ident=self.topic) | |
31 |
|
34 | |||
32 | def set_parent(self, parent): |
|
35 | def set_parent(self, parent): | |
@@ -48,7 +51,7 class ZMQShellDisplayHook(DisplayHook): | |||||
48 | self.parent_header = extract_header(parent) |
|
51 | self.parent_header = extract_header(parent) | |
49 |
|
52 | |||
50 | def start_displayhook(self): |
|
53 | def start_displayhook(self): | |
51 |
self.msg = self.session.msg(u' |
|
54 | self.msg = self.session.msg(u'execute_result', {}, parent=self.parent_header) | |
52 |
|
55 | |||
53 | def write_output_prompt(self): |
|
56 | def write_output_prompt(self): | |
54 | """Write the output prompt.""" |
|
57 | """Write the output prompt.""" |
@@ -1,12 +1,11 | |||||
1 | #!/usr/bin/env python |
|
|||
2 |
|
|
1 | """An interactive kernel that talks to frontends over 0MQ.""" | |
3 |
|
2 | |||
4 | #----------------------------------------------------------------------------- |
|
3 | # Copyright (c) IPython Development Team. | |
5 | # Imports |
|
4 | # Distributed under the terms of the Modified BSD License. | |
6 | #----------------------------------------------------------------------------- |
|
5 | ||
7 | from __future__ import print_function |
|
6 | from __future__ import print_function | |
8 |
|
7 | |||
9 | # Standard library imports |
|
8 | import getpass | |
10 | import sys |
|
9 | import sys | |
11 | import time |
|
10 | import time | |
12 | import traceback |
|
11 | import traceback | |
@@ -18,18 +17,17 from signal import ( | |||||
18 | signal, default_int_handler, SIGINT |
|
17 | signal, default_int_handler, SIGINT | |
19 | ) |
|
18 | ) | |
20 |
|
19 | |||
21 | # System library imports |
|
|||
22 | import zmq |
|
20 | import zmq | |
23 | from zmq.eventloop import ioloop |
|
21 | from zmq.eventloop import ioloop | |
24 | from zmq.eventloop.zmqstream import ZMQStream |
|
22 | from zmq.eventloop.zmqstream import ZMQStream | |
25 |
|
23 | |||
26 | # Local imports |
|
|||
27 | from IPython.config.configurable import Configurable |
|
24 | from IPython.config.configurable import Configurable | |
28 | from IPython.core.error import StdinNotImplementedError |
|
25 | from IPython.core.error import StdinNotImplementedError | |
29 | from IPython.core import release |
|
26 | from IPython.core import release | |
30 | from IPython.utils import py3compat |
|
27 | from IPython.utils import py3compat | |
31 | from IPython.utils.py3compat import builtin_mod, unicode_type, string_types |
|
28 | from IPython.utils.py3compat import builtin_mod, unicode_type, string_types | |
32 | from IPython.utils.jsonutil import json_clean |
|
29 | from IPython.utils.jsonutil import json_clean | |
|
30 | from IPython.utils.tokenutil import token_at_cursor | |||
33 | from IPython.utils.traitlets import ( |
|
31 | from IPython.utils.traitlets import ( | |
34 | Any, Instance, Float, Dict, List, Set, Integer, Unicode, |
|
32 | Any, Instance, Float, Dict, List, Set, Integer, Unicode, | |
35 | Type, Bool, |
|
33 | Type, Bool, | |
@@ -44,9 +42,9 from .zmqshell import ZMQInteractiveShell | |||||
44 | # Main kernel class |
|
42 | # Main kernel class | |
45 | #----------------------------------------------------------------------------- |
|
43 | #----------------------------------------------------------------------------- | |
46 |
|
44 | |||
47 |
protocol_version = |
|
45 | protocol_version = release.kernel_protocol_version | |
48 |
ipython_version = |
|
46 | ipython_version = release.version | |
49 |
language_version = |
|
47 | language_version = sys.version.split()[0] | |
50 |
|
48 | |||
51 |
|
49 | |||
52 | class Kernel(Configurable): |
|
50 | class Kernel(Configurable): | |
@@ -100,6 +98,10 class Kernel(Configurable): | |||||
100 | """ |
|
98 | """ | |
101 | ) |
|
99 | ) | |
102 |
|
100 | |||
|
101 | # track associations with current request | |||
|
102 | _allow_stdin = Bool(False) | |||
|
103 | _parent_header = Dict() | |||
|
104 | _parent_ident = Any(b'') | |||
103 | # Time to sleep after flushing the stdout/err buffers in each execute |
|
105 | # Time to sleep after flushing the stdout/err buffers in each execute | |
104 | # cycle. While this introduces a hard limit on the minimal latency of the |
|
106 | # cycle. While this introduces a hard limit on the minimal latency of the | |
105 | # execute cycle, it helps prevent output synchronization problems for |
|
107 | # execute cycle, it helps prevent output synchronization problems for | |
@@ -146,7 +148,7 class Kernel(Configurable): | |||||
146 | ) |
|
148 | ) | |
147 | self.shell.displayhook.session = self.session |
|
149 | self.shell.displayhook.session = self.session | |
148 | self.shell.displayhook.pub_socket = self.iopub_socket |
|
150 | self.shell.displayhook.pub_socket = self.iopub_socket | |
149 |
self.shell.displayhook.topic = self._topic(' |
|
151 | self.shell.displayhook.topic = self._topic('execute_result') | |
150 | self.shell.display_pub.session = self.session |
|
152 | self.shell.display_pub.session = self.session | |
151 | self.shell.display_pub.pub_socket = self.iopub_socket |
|
153 | self.shell.display_pub.pub_socket = self.iopub_socket | |
152 | self.shell.data_pub.session = self.session |
|
154 | self.shell.data_pub.session = self.session | |
@@ -157,7 +159,7 class Kernel(Configurable): | |||||
157 |
|
159 | |||
158 | # Build dict of handlers for message types |
|
160 | # Build dict of handlers for message types | |
159 | msg_types = [ 'execute_request', 'complete_request', |
|
161 | msg_types = [ 'execute_request', 'complete_request', | |
160 |
' |
|
162 | 'inspect_request', 'history_request', | |
161 | 'kernel_info_request', |
|
163 | 'kernel_info_request', | |
162 | 'connect_request', 'shutdown_request', |
|
164 | 'connect_request', 'shutdown_request', | |
163 | 'apply_request', |
|
165 | 'apply_request', | |
@@ -241,6 +243,7 class Kernel(Configurable): | |||||
241 | else: |
|
243 | else: | |
242 | # ensure default_int_handler during handler call |
|
244 | # ensure default_int_handler during handler call | |
243 | sig = signal(SIGINT, default_int_handler) |
|
245 | sig = signal(SIGINT, default_int_handler) | |
|
246 | self.log.debug("%s: %s", msg_type, msg) | |||
244 | try: |
|
247 | try: | |
245 | handler(stream, idents, msg) |
|
248 | handler(stream, idents, msg) | |
246 | except Exception: |
|
249 | except Exception: | |
@@ -320,12 +323,12 class Kernel(Configurable): | |||||
320 | new_md.update(other) |
|
323 | new_md.update(other) | |
321 | return new_md |
|
324 | return new_md | |
322 |
|
325 | |||
323 |
def _publish_ |
|
326 | def _publish_execute_input(self, code, parent, execution_count): | |
324 |
"""Publish the code request on the |
|
327 | """Publish the code request on the iopub stream.""" | |
325 |
|
328 | |||
326 |
self.session.send(self.iopub_socket, u' |
|
329 | self.session.send(self.iopub_socket, u'execute_input', | |
327 | {u'code':code, u'execution_count': execution_count}, |
|
330 | {u'code':code, u'execution_count': execution_count}, | |
328 |
parent=parent, ident=self._topic(' |
|
331 | parent=parent, ident=self._topic('execute_input') | |
329 | ) |
|
332 | ) | |
330 |
|
333 | |||
331 | def _publish_status(self, status, parent=None): |
|
334 | def _publish_status(self, status, parent=None): | |
@@ -337,6 +340,46 class Kernel(Configurable): | |||||
337 | ident=self._topic('status'), |
|
340 | ident=self._topic('status'), | |
338 | ) |
|
341 | ) | |
339 |
|
|
342 | ||
|
343 | def _forward_input(self, allow_stdin=False): | |||
|
344 | """Forward raw_input and getpass to the current frontend. | |||
|
345 | ||||
|
346 | via input_request | |||
|
347 | """ | |||
|
348 | self._allow_stdin = allow_stdin | |||
|
349 | ||||
|
350 | if py3compat.PY3: | |||
|
351 | self._sys_raw_input = builtin_mod.input | |||
|
352 | builtin_mod.input = self.raw_input | |||
|
353 | else: | |||
|
354 | self._sys_raw_input = builtin_mod.raw_input | |||
|
355 | self._sys_eval_input = builtin_mod.input | |||
|
356 | builtin_mod.raw_input = self.raw_input | |||
|
357 | builtin_mod.input = lambda prompt='': eval(self.raw_input(prompt)) | |||
|
358 | self._save_getpass = getpass.getpass | |||
|
359 | getpass.getpass = self.getpass | |||
|
360 | ||||
|
361 | def _restore_input(self): | |||
|
362 | """Restore raw_input, getpass""" | |||
|
363 | if py3compat.PY3: | |||
|
364 | builtin_mod.input = self._sys_raw_input | |||
|
365 | else: | |||
|
366 | builtin_mod.raw_input = self._sys_raw_input | |||
|
367 | builtin_mod.input = self._sys_eval_input | |||
|
368 | ||||
|
369 | getpass.getpass = self._save_getpass | |||
|
370 | ||||
|
371 | def set_parent(self, ident, parent): | |||
|
372 | """Set the current parent_header | |||
|
373 | ||||
|
374 | Side effects (IOPub messages) and replies are associated with | |||
|
375 | the request that caused them via the parent_header. | |||
|
376 | ||||
|
377 | The parent identity is used to route input_request messages | |||
|
378 | on the stdin channel. | |||
|
379 | """ | |||
|
380 | self._parent_ident = ident | |||
|
381 | self._parent_header = parent | |||
|
382 | self.shell.set_parent(parent) | |||
340 |
|
383 | |||
341 | def execute_request(self, stream, ident, parent): |
|
384 | def execute_request(self, stream, ident, parent): | |
342 | """handle an execute_request""" |
|
385 | """handle an execute_request""" | |
@@ -357,30 +400,14 class Kernel(Configurable): | |||||
357 |
|
400 | |||
358 | shell = self.shell # we'll need this a lot here |
|
401 | shell = self.shell # we'll need this a lot here | |
359 |
|
402 | |||
360 | # Replace raw_input. Note that is not sufficient to replace |
|
403 | self._forward_input(content.get('allow_stdin', False)) | |
361 | # raw_input in the user namespace. |
|
|||
362 | if content.get('allow_stdin', False): |
|
|||
363 | raw_input = lambda prompt='': self._raw_input(prompt, ident, parent) |
|
|||
364 | input = lambda prompt='': eval(raw_input(prompt)) |
|
|||
365 | else: |
|
|||
366 | raw_input = input = lambda prompt='' : self._no_raw_input() |
|
|||
367 |
|
||||
368 | if py3compat.PY3: |
|
|||
369 | self._sys_raw_input = builtin_mod.input |
|
|||
370 | builtin_mod.input = raw_input |
|
|||
371 | else: |
|
|||
372 | self._sys_raw_input = builtin_mod.raw_input |
|
|||
373 | self._sys_eval_input = builtin_mod.input |
|
|||
374 | builtin_mod.raw_input = raw_input |
|
|||
375 | builtin_mod.input = input |
|
|||
376 |
|
||||
377 | # Set the parent message of the display hook and out streams. |
|
404 | # Set the parent message of the display hook and out streams. | |
378 |
s |
|
405 | self.set_parent(ident, parent) | |
379 |
|
406 | |||
380 | # Re-broadcast our input for the benefit of listening clients, and |
|
407 | # Re-broadcast our input for the benefit of listening clients, and | |
381 | # start computing output |
|
408 | # start computing output | |
382 | if not silent: |
|
409 | if not silent: | |
383 |
self._publish_ |
|
410 | self._publish_execute_input(code, parent, shell.execution_count) | |
384 |
|
411 | |||
385 | reply_content = {} |
|
412 | reply_content = {} | |
386 | # FIXME: the shell calls the exception handler itself. |
|
413 | # FIXME: the shell calls the exception handler itself. | |
@@ -401,12 +428,7 class Kernel(Configurable): | |||||
401 | else: |
|
428 | else: | |
402 | status = u'ok' |
|
429 | status = u'ok' | |
403 | finally: |
|
430 | finally: | |
404 |
|
|
431 | self._restore_input() | |
405 | if py3compat.PY3: |
|
|||
406 | builtin_mod.input = self._sys_raw_input |
|
|||
407 | else: |
|
|||
408 | builtin_mod.raw_input = self._sys_raw_input |
|
|||
409 | builtin_mod.input = self._sys_eval_input |
|
|||
410 |
|
432 | |||
411 | reply_content[u'status'] = status |
|
433 | reply_content[u'status'] = status | |
412 |
|
434 | |||
@@ -427,16 +449,12 class Kernel(Configurable): | |||||
427 |
|
449 | |||
428 |
|
450 | |||
429 | # At this point, we can tell whether the main code execution succeeded |
|
451 | # At this point, we can tell whether the main code execution succeeded | |
430 |
# or not. If it did, we proceed to evaluate user_ |
|
452 | # or not. If it did, we proceed to evaluate user_expressions | |
431 | if reply_content['status'] == 'ok': |
|
453 | if reply_content['status'] == 'ok': | |
432 | reply_content[u'user_variables'] = \ |
|
|||
433 | shell.user_variables(content.get(u'user_variables', [])) |
|
|||
434 | reply_content[u'user_expressions'] = \ |
|
454 | reply_content[u'user_expressions'] = \ | |
435 | shell.user_expressions(content.get(u'user_expressions', {})) |
|
455 | shell.user_expressions(content.get(u'user_expressions', {})) | |
436 | else: |
|
456 | else: | |
437 |
# If there was an error, don't even try to compute |
|
457 | # If there was an error, don't even try to compute expressions | |
438 | # expressions |
|
|||
439 | reply_content[u'user_variables'] = {} |
|
|||
440 | reply_content[u'user_expressions'] = {} |
|
458 | reply_content[u'user_expressions'] = {} | |
441 |
|
459 | |||
442 | # Payloads should be retrieved regardless of outcome, so we can both |
|
460 | # Payloads should be retrieved regardless of outcome, so we can both | |
@@ -476,24 +494,41 class Kernel(Configurable): | |||||
476 | self._publish_status(u'idle', parent) |
|
494 | self._publish_status(u'idle', parent) | |
477 |
|
495 | |||
478 | def complete_request(self, stream, ident, parent): |
|
496 | def complete_request(self, stream, ident, parent): | |
479 | txt, matches = self._complete(parent) |
|
497 | content = parent['content'] | |
|
498 | code = content['code'] | |||
|
499 | cursor_pos = content['cursor_pos'] | |||
|
500 | ||||
|
501 | txt, matches = self.shell.complete('', code, cursor_pos) | |||
480 | matches = {'matches' : matches, |
|
502 | matches = {'matches' : matches, | |
481 |
' |
|
503 | 'cursor_end' : cursor_pos, | |
|
504 | 'cursor_start' : cursor_pos - len(txt), | |||
|
505 | 'metadata' : {}, | |||
482 | 'status' : 'ok'} |
|
506 | 'status' : 'ok'} | |
483 | matches = json_clean(matches) |
|
507 | matches = json_clean(matches) | |
484 | completion_msg = self.session.send(stream, 'complete_reply', |
|
508 | completion_msg = self.session.send(stream, 'complete_reply', | |
485 | matches, parent, ident) |
|
509 | matches, parent, ident) | |
486 | self.log.debug("%s", completion_msg) |
|
510 | self.log.debug("%s", completion_msg) | |
487 |
|
511 | |||
488 |
def |
|
512 | def inspect_request(self, stream, ident, parent): | |
489 | content = parent['content'] |
|
513 | content = parent['content'] | |
490 | object_info = self.shell.object_inspect(content['oname'], |
|
514 | ||
491 | detail_level = content.get('detail_level', 0) |
|
515 | name = token_at_cursor(content['code'], content['cursor_pos']) | |
|
516 | info = self.shell.object_inspect(name) | |||
|
517 | ||||
|
518 | reply_content = {'status' : 'ok'} | |||
|
519 | reply_content['data'] = data = {} | |||
|
520 | reply_content['metadata'] = {} | |||
|
521 | reply_content['found'] = info['found'] | |||
|
522 | if info['found']: | |||
|
523 | info_text = self.shell.object_inspect_text( | |||
|
524 | name, | |||
|
525 | detail_level=content.get('detail_level', 0), | |||
492 | ) |
|
526 | ) | |
|
527 | reply_content['data']['text/plain'] = info_text | |||
493 | # Before we send this object over, we scrub it for JSON usage |
|
528 | # Before we send this object over, we scrub it for JSON usage | |
494 |
|
|
529 | reply_content = json_clean(reply_content) | |
495 |
msg = self.session.send(stream, ' |
|
530 | msg = self.session.send(stream, 'inspect_reply', | |
496 |
|
|
531 | reply_content, parent, ident) | |
497 | self.log.debug("%s", msg) |
|
532 | self.log.debug("%s", msg) | |
498 |
|
533 | |||
499 | def history_request(self, stream, ident, parent): |
|
534 | def history_request(self, stream, ident, parent): | |
@@ -542,9 +577,11 class Kernel(Configurable): | |||||
542 | def kernel_info_request(self, stream, ident, parent): |
|
577 | def kernel_info_request(self, stream, ident, parent): | |
543 | vinfo = { |
|
578 | vinfo = { | |
544 | 'protocol_version': protocol_version, |
|
579 | 'protocol_version': protocol_version, | |
545 |
'i |
|
580 | 'implementation': 'ipython', | |
|
581 | 'implementation_version': ipython_version, | |||
546 | 'language_version': language_version, |
|
582 | 'language_version': language_version, | |
547 | 'language': 'python', |
|
583 | 'language': 'python', | |
|
584 | 'banner': self.shell.banner, | |||
548 | } |
|
585 | } | |
549 | msg = self.session.send(stream, 'kernel_info_reply', |
|
586 | msg = self.session.send(stream, 'kernel_info_reply', | |
550 | vinfo, parent, ident) |
|
587 | vinfo, parent, ident) | |
@@ -584,9 +621,6 class Kernel(Configurable): | |||||
584 | shell = self.shell |
|
621 | shell = self.shell | |
585 | shell.set_parent(parent) |
|
622 | shell.set_parent(parent) | |
586 |
|
623 | |||
587 | # pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent) |
|
|||
588 | # self.iopub_socket.send(pyin_msg) |
|
|||
589 | # self.session.send(self.iopub_socket, u'pyin', {u'code':code},parent=parent) |
|
|||
590 | md = self._make_metadata(parent['metadata']) |
|
624 | md = self._make_metadata(parent['metadata']) | |
591 | try: |
|
625 | try: | |
592 | working = shell.user_ns |
|
626 | working = shell.user_ns | |
@@ -631,8 +665,8 class Kernel(Configurable): | |||||
631 | # reset after use |
|
665 | # reset after use | |
632 | shell._reply_content = None |
|
666 | shell._reply_content = None | |
633 |
|
667 | |||
634 |
self.session.send(self.iopub_socket, u' |
|
668 | self.session.send(self.iopub_socket, u'error', reply_content, parent=parent, | |
635 |
ident=self._topic(' |
|
669 | ident=self._topic('error')) | |
636 | self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback'])) |
|
670 | self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback'])) | |
637 | result_buf = [] |
|
671 | result_buf = [] | |
638 |
|
672 | |||
@@ -735,7 +769,41 class Kernel(Configurable): | |||||
735 | raise StdinNotImplementedError("raw_input was called, but this " |
|
769 | raise StdinNotImplementedError("raw_input was called, but this " | |
736 | "frontend does not support stdin.") |
|
770 | "frontend does not support stdin.") | |
737 |
|
|
771 | ||
738 | def _raw_input(self, prompt, ident, parent): |
|
772 | def getpass(self, prompt=''): | |
|
773 | """Forward getpass to frontends | |||
|
774 | ||||
|
775 | Raises | |||
|
776 | ------ | |||
|
777 | StdinNotImplentedError if active frontend doesn't support stdin. | |||
|
778 | """ | |||
|
779 | if not self._allow_stdin: | |||
|
780 | raise StdinNotImplementedError( | |||
|
781 | "getpass was called, but this frontend does not support input requests." | |||
|
782 | ) | |||
|
783 | return self._input_request(prompt, | |||
|
784 | self._parent_ident, | |||
|
785 | self._parent_header, | |||
|
786 | password=True, | |||
|
787 | ) | |||
|
788 | ||||
|
789 | def raw_input(self, prompt=''): | |||
|
790 | """Forward raw_input to frontends | |||
|
791 | ||||
|
792 | Raises | |||
|
793 | ------ | |||
|
794 | StdinNotImplentedError if active frontend doesn't support stdin. | |||
|
795 | """ | |||
|
796 | if not self._allow_stdin: | |||
|
797 | raise StdinNotImplementedError( | |||
|
798 | "raw_input was called, but this frontend does not support input requests." | |||
|
799 | ) | |||
|
800 | return self._input_request(prompt, | |||
|
801 | self._parent_ident, | |||
|
802 | self._parent_header, | |||
|
803 | password=False, | |||
|
804 | ) | |||
|
805 | ||||
|
806 | def _input_request(self, prompt, ident, parent, password=False): | |||
739 | # Flush output before making the request. |
|
807 | # Flush output before making the request. | |
740 | sys.stderr.flush() |
|
808 | sys.stderr.flush() | |
741 | sys.stdout.flush() |
|
809 | sys.stdout.flush() | |
@@ -750,7 +818,7 class Kernel(Configurable): | |||||
750 | raise |
|
818 | raise | |
751 |
|
819 | |||
752 | # Send the input request. |
|
820 | # Send the input request. | |
753 | content = json_clean(dict(prompt=prompt)) |
|
821 | content = json_clean(dict(prompt=prompt, password=password)) | |
754 | self.session.send(self.stdin_socket, u'input_request', content, parent, |
|
822 | self.session.send(self.stdin_socket, u'input_request', content, parent, | |
755 | ident=ident) |
|
823 | ident=ident) | |
756 |
|
824 | |||
@@ -768,27 +836,13 class Kernel(Configurable): | |||||
768 | try: |
|
836 | try: | |
769 | value = py3compat.unicode_to_str(reply['content']['value']) |
|
837 | value = py3compat.unicode_to_str(reply['content']['value']) | |
770 | except: |
|
838 | except: | |
771 |
self.log.error(" |
|
839 | self.log.error("Bad input_reply: %s", parent) | |
772 | self.log.error("%s", parent) |
|
|||
773 | value = '' |
|
840 | value = '' | |
774 | if value == '\x04': |
|
841 | if value == '\x04': | |
775 | # EOF |
|
842 | # EOF | |
776 | raise EOFError |
|
843 | raise EOFError | |
777 | return value |
|
844 | return value | |
778 |
|
845 | |||
779 | def _complete(self, msg): |
|
|||
780 | c = msg['content'] |
|
|||
781 | try: |
|
|||
782 | cpos = int(c['cursor_pos']) |
|
|||
783 | except: |
|
|||
784 | # If we don't get something that we can convert to an integer, at |
|
|||
785 | # least attempt the completion guessing the cursor is at the end of |
|
|||
786 | # the text, if there's any, and otherwise of the line |
|
|||
787 | cpos = len(c['text']) |
|
|||
788 | if cpos==0: |
|
|||
789 | cpos = len(c['line']) |
|
|||
790 | return self.shell.complete(c['text'], c['line'], cpos) |
|
|||
791 |
|
||||
792 | def _at_shutdown(self): |
|
846 | def _at_shutdown(self): | |
793 | """Actions taken at shutdown by the kernel, called by python's atexit. |
|
847 | """Actions taken at shutdown by the kernel, called by python's atexit. | |
794 | """ |
|
848 | """ |
@@ -1,26 +1,19 | |||||
1 | """An Application for launching a kernel |
|
1 | """An Application for launching a kernel""" | |
2 | """ |
|
2 | ||
3 | # Copyright (c) IPython Development Team. |
|
3 | # Copyright (c) IPython Development Team. | |
4 | # Distributed under the terms of the Modified BSD License. |
|
4 | # Distributed under the terms of the Modified BSD License. | |
5 |
|
5 | |||
6 | #----------------------------------------------------------------------------- |
|
|||
7 | # Imports |
|
|||
8 | #----------------------------------------------------------------------------- |
|
|||
9 |
|
||||
10 | from __future__ import print_function |
|
6 | from __future__ import print_function | |
11 |
|
7 | |||
12 | # Standard library imports |
|
|||
13 | import atexit |
|
8 | import atexit | |
14 | import os |
|
9 | import os | |
15 | import sys |
|
10 | import sys | |
16 | import signal |
|
11 | import signal | |
17 |
|
12 | |||
18 | # System library imports |
|
|||
19 | import zmq |
|
13 | import zmq | |
20 | from zmq.eventloop import ioloop |
|
14 | from zmq.eventloop import ioloop | |
21 | from zmq.eventloop.zmqstream import ZMQStream |
|
15 | from zmq.eventloop.zmqstream import ZMQStream | |
22 |
|
16 | |||
23 | # IPython imports |
|
|||
24 | from IPython.core.ultratb import FormattedTB |
|
17 | from IPython.core.ultratb import FormattedTB | |
25 | from IPython.core.application import ( |
|
18 | from IPython.core.application import ( | |
26 | BaseIPythonApplication, base_flags, base_aliases, catch_config_error |
|
19 | BaseIPythonApplication, base_flags, base_aliases, catch_config_error | |
@@ -353,7 +346,7 class IPKernelApp(BaseIPythonApplication, InteractiveShellApp, | |||||
353 | shell = self.shell |
|
346 | shell = self.shell | |
354 | _showtraceback = shell._showtraceback |
|
347 | _showtraceback = shell._showtraceback | |
355 | try: |
|
348 | try: | |
356 |
# replace |
|
349 | # replace error-sending traceback with stderr | |
357 | def print_tb(etype, evalue, stb): |
|
350 | def print_tb(etype, evalue, stb): | |
358 | print ("GUI event loop or pylab initialization failed", |
|
351 | print ("GUI event loop or pylab initialization failed", | |
359 | file=io.stderr) |
|
352 | file=io.stderr) |
@@ -32,6 +32,7 from zmq.utils import jsonapi | |||||
32 | from zmq.eventloop.ioloop import IOLoop |
|
32 | from zmq.eventloop.ioloop import IOLoop | |
33 | from zmq.eventloop.zmqstream import ZMQStream |
|
33 | from zmq.eventloop.zmqstream import ZMQStream | |
34 |
|
34 | |||
|
35 | from IPython.core.release import kernel_protocol_version | |||
35 | from IPython.config.configurable import Configurable, LoggingConfigurable |
|
36 | from IPython.config.configurable import Configurable, LoggingConfigurable | |
36 | from IPython.utils import io |
|
37 | from IPython.utils import io | |
37 | from IPython.utils.importstring import import_item |
|
38 | from IPython.utils.importstring import import_item | |
@@ -182,6 +183,7 class Message(object): | |||||
182 |
|
183 | |||
183 | def msg_header(msg_id, msg_type, username, session): |
|
184 | def msg_header(msg_id, msg_type, username, session): | |
184 | date = datetime.now() |
|
185 | date = datetime.now() | |
|
186 | version = kernel_protocol_version | |||
185 | return locals() |
|
187 | return locals() | |
186 |
|
188 | |||
187 | def extract_header(msg_or_header): |
|
189 | def extract_header(msg_or_header): |
@@ -113,7 +113,7 def test_embed_kernel_basic(): | |||||
113 |
|
113 | |||
114 | with setup_kernel(cmd) as client: |
|
114 | with setup_kernel(cmd) as client: | |
115 | # oinfo a (int) |
|
115 | # oinfo a (int) | |
116 |
msg_id = client. |
|
116 | msg_id = client.inspect('a') | |
117 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) |
|
117 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) | |
118 | content = msg['content'] |
|
118 | content = msg['content'] | |
119 | nt.assert_true(content['found']) |
|
119 | nt.assert_true(content['found']) | |
@@ -124,11 +124,12 def test_embed_kernel_basic(): | |||||
124 | nt.assert_equal(content['status'], u'ok') |
|
124 | nt.assert_equal(content['status'], u'ok') | |
125 |
|
125 | |||
126 | # oinfo c (should be 10) |
|
126 | # oinfo c (should be 10) | |
127 |
msg_id = client. |
|
127 | msg_id = client.inspect('c') | |
128 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) |
|
128 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) | |
129 | content = msg['content'] |
|
129 | content = msg['content'] | |
130 | nt.assert_true(content['found']) |
|
130 | nt.assert_true(content['found']) | |
131 | nt.assert_equal(content['string_form'], u'10') |
|
131 | text = content['data']['text/plain'] | |
|
132 | nt.assert_in('10', text) | |||
132 |
|
133 | |||
133 | def test_embed_kernel_namespace(): |
|
134 | def test_embed_kernel_namespace(): | |
134 | """IPython.embed_kernel() inherits calling namespace""" |
|
135 | """IPython.embed_kernel() inherits calling namespace""" | |
@@ -144,21 +145,23 def test_embed_kernel_namespace(): | |||||
144 |
|
145 | |||
145 | with setup_kernel(cmd) as client: |
|
146 | with setup_kernel(cmd) as client: | |
146 | # oinfo a (int) |
|
147 | # oinfo a (int) | |
147 |
msg_id = client. |
|
148 | msg_id = client.inspect('a') | |
148 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) |
|
149 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) | |
149 | content = msg['content'] |
|
150 | content = msg['content'] | |
150 | nt.assert_true(content['found']) |
|
151 | nt.assert_true(content['found']) | |
151 | nt.assert_equal(content['string_form'], u'5') |
|
152 | text = content['data']['text/plain'] | |
|
153 | nt.assert_in(u'5', text) | |||
152 |
|
154 | |||
153 | # oinfo b (str) |
|
155 | # oinfo b (str) | |
154 |
msg_id = client. |
|
156 | msg_id = client.inspect('b') | |
155 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) |
|
157 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) | |
156 | content = msg['content'] |
|
158 | content = msg['content'] | |
157 | nt.assert_true(content['found']) |
|
159 | nt.assert_true(content['found']) | |
158 | nt.assert_equal(content['string_form'], u'hi there') |
|
160 | text = content['data']['text/plain'] | |
|
161 | nt.assert_in(u'hi there', text) | |||
159 |
|
162 | |||
160 | # oinfo c (undefined) |
|
163 | # oinfo c (undefined) | |
161 |
msg_id = client. |
|
164 | msg_id = client.inspect('c') | |
162 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) |
|
165 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) | |
163 | content = msg['content'] |
|
166 | content = msg['content'] | |
164 | nt.assert_false(content['found']) |
|
167 | nt.assert_false(content['found']) | |
@@ -180,11 +183,12 def test_embed_kernel_reentrant(): | |||||
180 |
|
183 | |||
181 | with setup_kernel(cmd) as client: |
|
184 | with setup_kernel(cmd) as client: | |
182 | for i in range(5): |
|
185 | for i in range(5): | |
183 |
msg_id = client. |
|
186 | msg_id = client.inspect('count') | |
184 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) |
|
187 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) | |
185 | content = msg['content'] |
|
188 | content = msg['content'] | |
186 | nt.assert_true(content['found']) |
|
189 | nt.assert_true(content['found']) | |
187 | nt.assert_equal(content['string_form'], unicode_type(i)) |
|
190 | text = content['data']['text/plain'] | |
|
191 | nt.assert_in(unicode_type(i), text) | |||
188 |
|
192 | |||
189 | # exit from embed_kernel |
|
193 | # exit from embed_kernel | |
190 | client.execute("get_ipython().exit_now = True") |
|
194 | client.execute("get_ipython().exit_now = True") |
@@ -10,22 +10,24 def test_ipython_start_kernel_userns(): | |||||
10 | 'start_kernel(user_ns=ns)') |
|
10 | 'start_kernel(user_ns=ns)') | |
11 |
|
11 | |||
12 | with setup_kernel(cmd) as client: |
|
12 | with setup_kernel(cmd) as client: | |
13 |
msg_id = client. |
|
13 | msg_id = client.inspect('tre') | |
14 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) |
|
14 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) | |
15 | content = msg['content'] |
|
15 | content = msg['content'] | |
16 | assert content['found'] |
|
16 | assert content['found'] | |
17 | nt.assert_equal(content['string_form'], u'123') |
|
17 | text = content['data']['text/plain'] | |
|
18 | nt.assert_in(u'123', text) | |||
18 |
|
19 | |||
19 | # user_module should be an instance of DummyMod |
|
20 | # user_module should be an instance of DummyMod | |
20 | msg_id = client.execute("usermod = get_ipython().user_module") |
|
21 | msg_id = client.execute("usermod = get_ipython().user_module") | |
21 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) |
|
22 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) | |
22 | content = msg['content'] |
|
23 | content = msg['content'] | |
23 | nt.assert_equal(content['status'], u'ok') |
|
24 | nt.assert_equal(content['status'], u'ok') | |
24 |
msg_id = client. |
|
25 | msg_id = client.inspect('usermod') | |
25 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) |
|
26 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) | |
26 | content = msg['content'] |
|
27 | content = msg['content'] | |
27 | assert content['found'] |
|
28 | assert content['found'] | |
28 | nt.assert_in('DummyMod', content['string_form']) |
|
29 | text = content['data']['text/plain'] | |
|
30 | nt.assert_in(u'DummyMod', text) | |||
29 |
|
31 | |||
30 | def test_ipython_start_kernel_no_userns(): |
|
32 | def test_ipython_start_kernel_no_userns(): | |
31 | # Issue #4188 - user_ns should be passed to shell as None, not {} |
|
33 | # Issue #4188 - user_ns should be passed to shell as None, not {} | |
@@ -38,8 +40,9 def test_ipython_start_kernel_no_userns(): | |||||
38 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) |
|
40 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) | |
39 | content = msg['content'] |
|
41 | content = msg['content'] | |
40 | nt.assert_equal(content['status'], u'ok') |
|
42 | nt.assert_equal(content['status'], u'ok') | |
41 |
msg_id = client. |
|
43 | msg_id = client.inspect('usermod') | |
42 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) |
|
44 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) | |
43 | content = msg['content'] |
|
45 | content = msg['content'] | |
44 | assert content['found'] |
|
46 | assert content['found'] | |
45 | nt.assert_not_in('DummyMod', content['string_form']) |
|
47 | text = content['data']['text/plain'] | |
|
48 | nt.assert_not_in(u'DummyMod', text) |
@@ -10,20 +10,18 implementation that doesn't rely on so much monkeypatching. | |||||
10 | But this lets us maintain a fully working IPython as we develop the new |
|
10 | But this lets us maintain a fully working IPython as we develop the new | |
11 | machinery. This should thus be thought of as scaffolding. |
|
11 | machinery. This should thus be thought of as scaffolding. | |
12 | """ |
|
12 | """ | |
13 | #----------------------------------------------------------------------------- |
|
13 | ||
14 | # Imports |
|
14 | # Copyright (c) IPython Development Team. | |
15 | #----------------------------------------------------------------------------- |
|
15 | # Distributed under the terms of the Modified BSD License. | |
|
16 | ||||
16 | from __future__ import print_function |
|
17 | from __future__ import print_function | |
17 |
|
18 | |||
18 | # Stdlib |
|
|||
19 | import os |
|
19 | import os | |
20 | import sys |
|
20 | import sys | |
21 | import time |
|
21 | import time | |
22 |
|
22 | |||
23 | # System library imports |
|
|||
24 | from zmq.eventloop import ioloop |
|
23 | from zmq.eventloop import ioloop | |
25 |
|
24 | |||
26 | # Our own |
|
|||
27 | from IPython.core.interactiveshell import ( |
|
25 | from IPython.core.interactiveshell import ( | |
28 | InteractiveShell, InteractiveShellABC |
|
26 | InteractiveShell, InteractiveShellABC | |
29 | ) |
|
27 | ) | |
@@ -34,6 +32,7 from IPython.core.error import UsageError | |||||
34 | from IPython.core.magics import MacroToEdit, CodeMagics |
|
32 | from IPython.core.magics import MacroToEdit, CodeMagics | |
35 | from IPython.core.magic import magics_class, line_magic, Magics |
|
33 | from IPython.core.magic import magics_class, line_magic, Magics | |
36 | from IPython.core.payloadpage import install_payload_page |
|
34 | from IPython.core.payloadpage import install_payload_page | |
|
35 | from IPython.core.usage import default_gui_banner | |||
37 | from IPython.display import display, Javascript |
|
36 | from IPython.display import display, Javascript | |
38 | from IPython.kernel.inprocess.socket import SocketABC |
|
37 | from IPython.kernel.inprocess.socket import SocketABC | |
39 | from IPython.kernel import ( |
|
38 | from IPython.kernel import ( | |
@@ -74,13 +73,12 class ZMQDisplayPublisher(DisplayPublisher): | |||||
74 | sys.stdout.flush() |
|
73 | sys.stdout.flush() | |
75 | sys.stderr.flush() |
|
74 | sys.stderr.flush() | |
76 |
|
75 | |||
77 |
def publish(self, |
|
76 | def publish(self, data, metadata=None, source=None): | |
78 | self._flush_streams() |
|
77 | self._flush_streams() | |
79 | if metadata is None: |
|
78 | if metadata is None: | |
80 | metadata = {} |
|
79 | metadata = {} | |
81 |
self._validate_data( |
|
80 | self._validate_data(data, metadata) | |
82 | content = {} |
|
81 | content = {} | |
83 | content['source'] = source |
|
|||
84 | content['data'] = encode_images(data) |
|
82 | content['data'] = encode_images(data) | |
85 | content['metadata'] = metadata |
|
83 | content['metadata'] = metadata | |
86 | self.session.send( |
|
84 | self.session.send( | |
@@ -425,6 +423,9 class ZMQInteractiveShell(InteractiveShell): | |||||
425 | kernel = Any() |
|
423 | kernel = Any() | |
426 | parent_header = Any() |
|
424 | parent_header = Any() | |
427 |
|
425 | |||
|
426 | def _banner1_default(self): | |||
|
427 | return default_gui_banner | |||
|
428 | ||||
428 | # Override the traitlet in the parent class, because there's no point using |
|
429 | # Override the traitlet in the parent class, because there's no point using | |
429 | # readline for the kernel. Can be removed when the readline code is moved |
|
430 | # readline for the kernel. Can be removed when the readline code is moved | |
430 | # to the terminal frontend. |
|
431 | # to the terminal frontend. | |
@@ -512,9 +513,9 class ZMQInteractiveShell(InteractiveShell): | |||||
512 | # to pick up |
|
513 | # to pick up | |
513 | topic = None |
|
514 | topic = None | |
514 | if dh.topic: |
|
515 | if dh.topic: | |
515 |
topic = dh.topic.replace(b' |
|
516 | topic = dh.topic.replace(b'execute_result', b'error') | |
516 |
|
517 | |||
517 |
exc_msg = dh.session.send(dh.pub_socket, u' |
|
518 | exc_msg = dh.session.send(dh.pub_socket, u'error', json_clean(exc_content), dh.parent_header, ident=topic) | |
518 |
|
519 | |||
519 | # FIXME - Hack: store exception info in shell object. Right now, the |
|
520 | # FIXME - Hack: store exception info in shell object. Right now, the | |
520 | # caller is reading this info after the fact, we need to fix this logic |
|
521 | # caller is reading this info after the fact, we need to fix this logic |
@@ -1,19 +1,7 | |||||
1 | """AsyncResult objects for the client |
|
1 | """AsyncResult objects for the client""" | |
2 |
|
2 | |||
3 | Authors: |
|
3 | # Copyright (c) IPython Development Team. | |
4 |
|
4 | # Distributed under the terms of the Modified BSD License. | ||
5 | * MinRK |
|
|||
6 | """ |
|
|||
7 | #----------------------------------------------------------------------------- |
|
|||
8 | # Copyright (C) 2010-2011 The IPython Development Team |
|
|||
9 | # |
|
|||
10 | # Distributed under the terms of the BSD License. The full license is in |
|
|||
11 | # the file COPYING, distributed as part of this software. |
|
|||
12 | #----------------------------------------------------------------------------- |
|
|||
13 |
|
||||
14 | #----------------------------------------------------------------------------- |
|
|||
15 | # Imports |
|
|||
16 | #----------------------------------------------------------------------------- |
|
|||
17 |
|
5 | |||
18 | from __future__ import print_function |
|
6 | from __future__ import print_function | |
19 |
|
7 | |||
@@ -436,7 +424,7 class AsyncResult(object): | |||||
436 | for output in self.outputs: |
|
424 | for output in self.outputs: | |
437 | self._republish_displaypub(output, self.engine_id) |
|
425 | self._republish_displaypub(output, self.engine_id) | |
438 |
|
426 | |||
439 |
if self. |
|
427 | if self.execute_result is not None: | |
440 | display(self.get()) |
|
428 | display(self.get()) | |
441 |
|
429 | |||
442 | def _wait_for_outputs(self, timeout=-1): |
|
430 | def _wait_for_outputs(self, timeout=-1): | |
@@ -495,15 +483,15 class AsyncResult(object): | |||||
495 |
|
483 | |||
496 | stdouts = self.stdout |
|
484 | stdouts = self.stdout | |
497 | stderrs = self.stderr |
|
485 | stderrs = self.stderr | |
498 | pyouts = self.pyout |
|
486 | execute_results = self.execute_result | |
499 | output_lists = self.outputs |
|
487 | output_lists = self.outputs | |
500 | results = self.get() |
|
488 | results = self.get() | |
501 |
|
489 | |||
502 | targets = self.engine_id |
|
490 | targets = self.engine_id | |
503 |
|
491 | |||
504 | if groupby == "engine": |
|
492 | if groupby == "engine": | |
505 |
for eid,stdout,stderr,outputs,r, |
|
493 | for eid,stdout,stderr,outputs,r,execute_result in zip( | |
506 |
targets, stdouts, stderrs, output_lists, results, |
|
494 | targets, stdouts, stderrs, output_lists, results, execute_results | |
507 | ): |
|
495 | ): | |
508 | self._display_stream(stdout, '[stdout:%i] ' % eid) |
|
496 | self._display_stream(stdout, '[stdout:%i] ' % eid) | |
509 | self._display_stream(stderr, '[stderr:%i] ' % eid, file=sys.stderr) |
|
497 | self._display_stream(stderr, '[stderr:%i] ' % eid, file=sys.stderr) | |
@@ -514,13 +502,13 class AsyncResult(object): | |||||
514 | # displaypub is meaningless outside IPython |
|
502 | # displaypub is meaningless outside IPython | |
515 | return |
|
503 | return | |
516 |
|
504 | |||
517 |
if outputs or |
|
505 | if outputs or execute_result is not None: | |
518 | _raw_text('[output:%i]' % eid) |
|
506 | _raw_text('[output:%i]' % eid) | |
519 |
|
507 | |||
520 | for output in outputs: |
|
508 | for output in outputs: | |
521 | self._republish_displaypub(output, eid) |
|
509 | self._republish_displaypub(output, eid) | |
522 |
|
510 | |||
523 |
if |
|
511 | if execute_result is not None: | |
524 | display(r) |
|
512 | display(r) | |
525 |
|
513 | |||
526 | elif groupby in ('type', 'order'): |
|
514 | elif groupby in ('type', 'order'): | |
@@ -555,9 +543,9 class AsyncResult(object): | |||||
555 | for output in outputs: |
|
543 | for output in outputs: | |
556 | self._republish_displaypub(output, eid) |
|
544 | self._republish_displaypub(output, eid) | |
557 |
|
545 | |||
558 |
# finally, add |
|
546 | # finally, add execute_result: | |
559 |
for eid,r, |
|
547 | for eid,r,execute_result in zip(targets, results, execute_results): | |
560 |
if |
|
548 | if execute_result is not None: | |
561 | display(r) |
|
549 | display(r) | |
562 |
|
550 | |||
563 | else: |
|
551 | else: |
@@ -1,20 +1,9 | |||||
1 |
"""A semi-synchronous Client for |
|
1 | """A semi-synchronous Client for IPython parallel""" | |
2 |
|
2 | |||
3 | Authors: |
|
3 | # Copyright (c) IPython Development Team. | |
|
4 | # Distributed under the terms of the Modified BSD License. | |||
4 |
|
5 | |||
5 | * MinRK |
|
|||
6 | """ |
|
|||
7 | from __future__ import print_function |
|
6 | from __future__ import print_function | |
8 | #----------------------------------------------------------------------------- |
|
|||
9 | # Copyright (C) 2010-2011 The IPython Development Team |
|
|||
10 | # |
|
|||
11 | # Distributed under the terms of the BSD License. The full license is in |
|
|||
12 | # the file COPYING, distributed as part of this software. |
|
|||
13 | #----------------------------------------------------------------------------- |
|
|||
14 |
|
||||
15 | #----------------------------------------------------------------------------- |
|
|||
16 | # Imports |
|
|||
17 | #----------------------------------------------------------------------------- |
|
|||
18 |
|
7 | |||
19 | import os |
|
8 | import os | |
20 | import json |
|
9 | import json | |
@@ -29,7 +18,6 from pprint import pprint | |||||
29 | pjoin = os.path.join |
|
18 | pjoin = os.path.join | |
30 |
|
19 | |||
31 | import zmq |
|
20 | import zmq | |
32 | # from zmq.eventloop import ioloop, zmqstream |
|
|||
33 |
|
21 | |||
34 | from IPython.config.configurable import MultipleInstanceError |
|
22 | from IPython.config.configurable import MultipleInstanceError | |
35 | from IPython.core.application import BaseIPythonApplication |
|
23 | from IPython.core.application import BaseIPythonApplication | |
@@ -84,25 +72,25 class ExecuteReply(RichOutput): | |||||
84 |
|
72 | |||
85 | @property |
|
73 | @property | |
86 | def source(self): |
|
74 | def source(self): | |
87 |
|
|
75 | execute_result = self.metadata['execute_result'] | |
88 |
if |
|
76 | if execute_result: | |
89 |
return |
|
77 | return execute_result.get('source', '') | |
90 |
|
78 | |||
91 | @property |
|
79 | @property | |
92 | def data(self): |
|
80 | def data(self): | |
93 |
|
|
81 | execute_result = self.metadata['execute_result'] | |
94 |
if |
|
82 | if execute_result: | |
95 |
return |
|
83 | return execute_result.get('data', {}) | |
96 |
|
84 | |||
97 | @property |
|
85 | @property | |
98 | def _metadata(self): |
|
86 | def _metadata(self): | |
99 |
|
|
87 | execute_result = self.metadata['execute_result'] | |
100 |
if |
|
88 | if execute_result: | |
101 |
return |
|
89 | return execute_result.get('metadata', {}) | |
102 |
|
90 | |||
103 | def display(self): |
|
91 | def display(self): | |
104 | from IPython.display import publish_display_data |
|
92 | from IPython.display import publish_display_data | |
105 |
publish_display_data( |
|
93 | publish_display_data(self.data, self.metadata) | |
106 |
|
94 | |||
107 | def _repr_mime_(self, mime): |
|
95 | def _repr_mime_(self, mime): | |
108 | if mime not in self.data: |
|
96 | if mime not in self.data: | |
@@ -122,16 +110,16 class ExecuteReply(RichOutput): | |||||
122 | return self.metadata[key] |
|
110 | return self.metadata[key] | |
123 |
|
111 | |||
124 | def __repr__(self): |
|
112 | def __repr__(self): | |
125 |
|
|
113 | execute_result = self.metadata['execute_result'] or {'data':{}} | |
126 |
text_out = |
|
114 | text_out = execute_result['data'].get('text/plain', '') | |
127 | if len(text_out) > 32: |
|
115 | if len(text_out) > 32: | |
128 | text_out = text_out[:29] + '...' |
|
116 | text_out = text_out[:29] + '...' | |
129 |
|
117 | |||
130 | return "<ExecuteReply[%i]: %s>" % (self.execution_count, text_out) |
|
118 | return "<ExecuteReply[%i]: %s>" % (self.execution_count, text_out) | |
131 |
|
119 | |||
132 | def _repr_pretty_(self, p, cycle): |
|
120 | def _repr_pretty_(self, p, cycle): | |
133 |
|
|
121 | execute_result = self.metadata['execute_result'] or {'data':{}} | |
134 |
text_out = |
|
122 | text_out = execute_result['data'].get('text/plain', '') | |
135 |
|
123 | |||
136 | if not text_out: |
|
124 | if not text_out: | |
137 | return |
|
125 | return | |
@@ -181,9 +169,9 class Metadata(dict): | |||||
181 | 'after' : None, |
|
169 | 'after' : None, | |
182 | 'status' : None, |
|
170 | 'status' : None, | |
183 |
|
171 | |||
184 |
' |
|
172 | 'execute_input' : None, | |
185 |
' |
|
173 | 'execute_result' : None, | |
186 |
' |
|
174 | 'error' : None, | |
187 | 'stdout' : '', |
|
175 | 'stdout' : '', | |
188 | 'stderr' : '', |
|
176 | 'stderr' : '', | |
189 | 'outputs' : [], |
|
177 | 'outputs' : [], | |
@@ -881,14 +869,14 class Client(HasTraits): | |||||
881 | name = content['name'] |
|
869 | name = content['name'] | |
882 | s = md[name] or '' |
|
870 | s = md[name] or '' | |
883 | md[name] = s + content['data'] |
|
871 | md[name] = s + content['data'] | |
884 |
elif msg_type == ' |
|
872 | elif msg_type == 'error': | |
885 |
md.update({' |
|
873 | md.update({'error' : self._unwrap_exception(content)}) | |
886 |
elif msg_type == ' |
|
874 | elif msg_type == 'execute_input': | |
887 |
md.update({' |
|
875 | md.update({'execute_input' : content['code']}) | |
888 | elif msg_type == 'display_data': |
|
876 | elif msg_type == 'display_data': | |
889 | md['outputs'].append(content) |
|
877 | md['outputs'].append(content) | |
890 |
elif msg_type == ' |
|
878 | elif msg_type == 'execute_result': | |
891 |
md[' |
|
879 | md['execute_result'] = content | |
892 | elif msg_type == 'data_message': |
|
880 | elif msg_type == 'data_message': | |
893 | data, remainder = serialize.unserialize_object(msg['buffers']) |
|
881 | data, remainder = serialize.unserialize_object(msg['buffers']) | |
894 | md['data'].update(data) |
|
882 | md['data'].update(data) | |
@@ -1297,7 +1285,7 class Client(HasTraits): | |||||
1297 | if not isinstance(metadata, dict): |
|
1285 | if not isinstance(metadata, dict): | |
1298 | raise TypeError("metadata must be dict, not %s" % type(metadata)) |
|
1286 | raise TypeError("metadata must be dict, not %s" % type(metadata)) | |
1299 |
|
1287 | |||
1300 |
content = dict(code=code, silent=bool(silent), |
|
1288 | content = dict(code=code, silent=bool(silent), user_expressions={}) | |
1301 |
|
1289 | |||
1302 |
|
1290 | |||
1303 | msg = self.session.send(socket, "execute_request", content=content, ident=ident, |
|
1291 | msg = self.session.send(socket, "execute_request", content=content, ident=ident, |
@@ -1,21 +1,12 | |||||
1 | """The IPython Controller Hub with 0MQ |
|
1 | """The IPython Controller Hub with 0MQ | |
|
2 | ||||
2 | This is the master object that handles connections from engines and clients, |
|
3 | This is the master object that handles connections from engines and clients, | |
3 | and monitors traffic through the various queues. |
|
4 | and monitors traffic through the various queues. | |
4 |
|
||||
5 | Authors: |
|
|||
6 |
|
||||
7 | * Min RK |
|
|||
8 | """ |
|
5 | """ | |
9 | #----------------------------------------------------------------------------- |
|
|||
10 | # Copyright (C) 2010-2011 The IPython Development Team |
|
|||
11 | # |
|
|||
12 | # Distributed under the terms of the BSD License. The full license is in |
|
|||
13 | # the file COPYING, distributed as part of this software. |
|
|||
14 | #----------------------------------------------------------------------------- |
|
|||
15 |
|
6 | |||
16 | #----------------------------------------------------------------------------- |
|
7 | # Copyright (c) IPython Development Team. | |
17 | # Imports |
|
8 | # Distributed under the terms of the Modified BSD License. | |
18 | #----------------------------------------------------------------------------- |
|
9 | ||
19 | from __future__ import print_function |
|
10 | from __future__ import print_function | |
20 |
|
11 | |||
21 | import json |
|
12 | import json | |
@@ -75,9 +66,9 def empty_record(): | |||||
75 | 'result_content' : None, |
|
66 | 'result_content' : None, | |
76 | 'result_buffers' : None, |
|
67 | 'result_buffers' : None, | |
77 | 'queue' : None, |
|
68 | 'queue' : None, | |
78 |
' |
|
69 | 'execute_input' : None, | |
79 |
' |
|
70 | 'execute_result': None, | |
80 |
' |
|
71 | 'error': None, | |
81 | 'stdout': '', |
|
72 | 'stdout': '', | |
82 | 'stderr': '', |
|
73 | 'stderr': '', | |
83 | } |
|
74 | } | |
@@ -103,9 +94,9 def init_record(msg): | |||||
103 | 'result_content' : None, |
|
94 | 'result_content' : None, | |
104 | 'result_buffers' : None, |
|
95 | 'result_buffers' : None, | |
105 | 'queue' : None, |
|
96 | 'queue' : None, | |
106 |
' |
|
97 | 'execute_input' : None, | |
107 |
' |
|
98 | 'execute_result': None, | |
108 |
' |
|
99 | 'error': None, | |
109 | 'stdout': '', |
|
100 | 'stdout': '', | |
110 | 'stderr': '', |
|
101 | 'stderr': '', | |
111 | } |
|
102 | } | |
@@ -874,11 +865,11 class Hub(SessionFactory): | |||||
874 | s = rec[name] or '' |
|
865 | s = rec[name] or '' | |
875 | d[name] = s + content['data'] |
|
866 | d[name] = s + content['data'] | |
876 |
|
867 | |||
877 |
elif msg_type == ' |
|
868 | elif msg_type == 'error': | |
878 |
d[' |
|
869 | d['error'] = content | |
879 |
elif msg_type == ' |
|
870 | elif msg_type == 'execute_input': | |
880 |
d[' |
|
871 | d['execute_input'] = content['code'] | |
881 |
elif msg_type in ('display_data', ' |
|
872 | elif msg_type in ('display_data', 'execute_result'): | |
882 | d[msg_type] = content |
|
873 | d[msg_type] = content | |
883 | elif msg_type == 'status': |
|
874 | elif msg_type == 'status': | |
884 | pass |
|
875 | pass | |
@@ -1325,7 +1316,7 class Hub(SessionFactory): | |||||
1325 | def _extract_record(self, rec): |
|
1316 | def _extract_record(self, rec): | |
1326 | """decompose a TaskRecord dict into subsection of reply for get_result""" |
|
1317 | """decompose a TaskRecord dict into subsection of reply for get_result""" | |
1327 | io_dict = {} |
|
1318 | io_dict = {} | |
1328 |
for key in (' |
|
1319 | for key in ('execute_input', 'execute_result', 'error', 'stdout', 'stderr'): | |
1329 | io_dict[key] = rec[key] |
|
1320 | io_dict[key] = rec[key] | |
1330 | content = { |
|
1321 | content = { | |
1331 | 'header': rec['header'], |
|
1322 | 'header': rec['header'], |
@@ -1,15 +1,7 | |||||
1 | """A TaskRecord backend using sqlite3 |
|
1 | """A TaskRecord backend using sqlite3""" | |
2 |
|
2 | |||
3 | Authors: |
|
3 | # Copyright (c) IPython Development Team. | |
4 |
|
4 | # Distributed under the terms of the Modified BSD License. | ||
5 | * Min RK |
|
|||
6 | """ |
|
|||
7 | #----------------------------------------------------------------------------- |
|
|||
8 | # Copyright (C) 2011 The IPython Development Team |
|
|||
9 | # |
|
|||
10 | # Distributed under the terms of the BSD License. The full license is in |
|
|||
11 | # the file COPYING, distributed as part of this software. |
|
|||
12 | #----------------------------------------------------------------------------- |
|
|||
13 |
|
5 | |||
14 | import json |
|
6 | import json | |
15 | import os |
|
7 | import os | |
@@ -128,9 +120,9 class SQLiteDB(BaseDB): | |||||
128 | 'result_content' , |
|
120 | 'result_content' , | |
129 | 'result_buffers' , |
|
121 | 'result_buffers' , | |
130 | 'queue' , |
|
122 | 'queue' , | |
131 |
' |
|
123 | 'execute_input' , | |
132 |
' |
|
124 | 'execute_result', | |
133 |
' |
|
125 | 'error', | |
134 | 'stdout', |
|
126 | 'stdout', | |
135 | 'stderr', |
|
127 | 'stderr', | |
136 | ]) |
|
128 | ]) | |
@@ -152,9 +144,9 class SQLiteDB(BaseDB): | |||||
152 | 'result_content' : 'dict text', |
|
144 | 'result_content' : 'dict text', | |
153 | 'result_buffers' : 'bufs blob', |
|
145 | 'result_buffers' : 'bufs blob', | |
154 | 'queue' : 'text', |
|
146 | 'queue' : 'text', | |
155 |
' |
|
147 | 'execute_input' : 'text', | |
156 |
' |
|
148 | 'execute_result' : 'text', | |
157 |
' |
|
149 | 'error' : 'text', | |
158 | 'stdout' : 'text', |
|
150 | 'stdout' : 'text', | |
159 | 'stderr' : 'text', |
|
151 | 'stderr' : 'text', | |
160 | }) |
|
152 | }) | |
@@ -263,9 +255,9 class SQLiteDB(BaseDB): | |||||
263 | result_content dict text, |
|
255 | result_content dict text, | |
264 | result_buffers bufs blob, |
|
256 | result_buffers bufs blob, | |
265 | queue text, |
|
257 | queue text, | |
266 |
|
|
258 | execute_input text, | |
267 |
|
|
259 | execute_result text, | |
268 |
|
|
260 | error text, | |
269 | stdout text, |
|
261 | stdout text, | |
270 | stderr text) |
|
262 | stderr text) | |
271 | """%self.table) |
|
263 | """%self.table) |
@@ -1,17 +1,10 | |||||
1 | """A simple engine that talks to a controller over 0MQ. |
|
1 | """A simple engine that talks to a controller over 0MQ. | |
2 | it handles registration, etc. and launches a kernel |
|
2 | it handles registration, etc. and launches a kernel | |
3 | connected to the Controller's Schedulers. |
|
3 | connected to the Controller's Schedulers. | |
4 |
|
||||
5 | Authors: |
|
|||
6 |
|
||||
7 | * Min RK |
|
|||
8 | """ |
|
4 | """ | |
9 | #----------------------------------------------------------------------------- |
|
5 | ||
10 |
# |
|
6 | # Copyright (c) IPython Development Team. | |
11 | # |
|
7 | # Distributed under the terms of the Modified BSD License. | |
12 | # Distributed under the terms of the BSD License. The full license is in |
|
|||
13 | # the file COPYING, distributed as part of this software. |
|
|||
14 | #----------------------------------------------------------------------------- |
|
|||
15 |
|
8 | |||
16 | from __future__ import print_function |
|
9 | from __future__ import print_function | |
17 |
|
10 | |||
@@ -228,7 +221,7 class EngineFactory(RegistrationFactory): | |||||
228 | sys.stderr.topic = cast_bytes('engine.%i.stderr' % self.id) |
|
221 | sys.stderr.topic = cast_bytes('engine.%i.stderr' % self.id) | |
229 | if self.display_hook_factory: |
|
222 | if self.display_hook_factory: | |
230 | sys.displayhook = self.display_hook_factory(self.session, iopub_socket) |
|
223 | sys.displayhook = self.display_hook_factory(self.session, iopub_socket) | |
231 |
sys.displayhook.topic = cast_bytes('engine.%i. |
|
224 | sys.displayhook.topic = cast_bytes('engine.%i.execute_result' % self.id) | |
232 |
|
225 | |||
233 | self.kernel = Kernel(parent=self, int_id=self.id, ident=self.ident, session=self.session, |
|
226 | self.kernel = Kernel(parent=self, int_id=self.id, ident=self.ident, session=self.session, | |
234 | control_stream=control_stream, shell_streams=shell_streams, iopub_socket=iopub_socket, |
|
227 | control_stream=control_stream, shell_streams=shell_streams, iopub_socket=iopub_socket, |
@@ -1,20 +1,7 | |||||
1 | """Tests for parallel client.py |
|
1 | """Tests for parallel client.py""" | |
2 |
|
2 | |||
3 | Authors: |
|
3 | # Copyright (c) IPython Development Team. | |
4 |
|
4 | # Distributed under the terms of the Modified BSD License. | ||
5 | * Min RK |
|
|||
6 | """ |
|
|||
7 |
|
||||
8 | #------------------------------------------------------------------------------- |
|
|||
9 | # Copyright (C) 2011 The IPython Development Team |
|
|||
10 | # |
|
|||
11 | # Distributed under the terms of the BSD License. The full license is in |
|
|||
12 | # the file COPYING, distributed as part of this software. |
|
|||
13 | #------------------------------------------------------------------------------- |
|
|||
14 |
|
||||
15 | #------------------------------------------------------------------------------- |
|
|||
16 | # Imports |
|
|||
17 | #------------------------------------------------------------------------------- |
|
|||
18 |
|
5 | |||
19 | from __future__ import division |
|
6 | from __future__ import division | |
20 |
|
7 | |||
@@ -177,7 +164,7 class TestClient(ClusterTestCase): | |||||
177 | time.sleep(.25) |
|
164 | time.sleep(.25) | |
178 | ahr = self.client.get_result(ar.msg_ids[0]) |
|
165 | ahr = self.client.get_result(ar.msg_ids[0]) | |
179 | self.assertTrue(isinstance(ahr, AsyncHubResult)) |
|
166 | self.assertTrue(isinstance(ahr, AsyncHubResult)) | |
180 |
self.assertEqual(ahr.get(). |
|
167 | self.assertEqual(ahr.get().execute_result, ar.get().execute_result) | |
181 | ar2 = self.client.get_result(ar.msg_ids[0]) |
|
168 | ar2 = self.client.get_result(ar.msg_ids[0]) | |
182 | self.assertFalse(isinstance(ar2, AsyncHubResult)) |
|
169 | self.assertFalse(isinstance(ar2, AsyncHubResult)) | |
183 | c.close() |
|
170 | c.close() |
@@ -537,7 +537,7 class TestView(ClusterTestCase): | |||||
537 | ar = e0.execute("5", silent=False) |
|
537 | ar = e0.execute("5", silent=False) | |
538 | er = ar.get() |
|
538 | er = ar.get() | |
539 | self.assertEqual(str(er), "<ExecuteReply[%i]: 5>" % er.execution_count) |
|
539 | self.assertEqual(str(er), "<ExecuteReply[%i]: 5>" % er.execution_count) | |
540 |
self.assertEqual(er. |
|
540 | self.assertEqual(er.execute_result['data']['text/plain'], '5') | |
541 |
|
541 | |||
542 | def test_execute_reply_rich(self): |
|
542 | def test_execute_reply_rich(self): | |
543 | e0 = self.client[self.client.ids[0]] |
|
543 | e0 = self.client[self.client.ids[0]] | |
@@ -558,21 +558,21 class TestView(ClusterTestCase): | |||||
558 | er = ar.get() |
|
558 | er = ar.get() | |
559 | self.assertEqual(er.stdout.strip(), '5') |
|
559 | self.assertEqual(er.stdout.strip(), '5') | |
560 |
|
560 | |||
561 |
def test_execute_ |
|
561 | def test_execute_result(self): | |
562 |
"""execute triggers |
|
562 | """execute triggers execute_result with silent=False""" | |
563 | view = self.client[:] |
|
563 | view = self.client[:] | |
564 | ar = view.execute("5", silent=False, block=True) |
|
564 | ar = view.execute("5", silent=False, block=True) | |
565 |
|
565 | |||
566 | expected = [{'text/plain' : '5'}] * len(view) |
|
566 | expected = [{'text/plain' : '5'}] * len(view) | |
567 |
mimes = [ out['data'] for out in ar. |
|
567 | mimes = [ out['data'] for out in ar.execute_result ] | |
568 | self.assertEqual(mimes, expected) |
|
568 | self.assertEqual(mimes, expected) | |
569 |
|
569 | |||
570 | def test_execute_silent(self): |
|
570 | def test_execute_silent(self): | |
571 |
"""execute does not trigger |
|
571 | """execute does not trigger execute_result with silent=True""" | |
572 | view = self.client[:] |
|
572 | view = self.client[:] | |
573 | ar = view.execute("5", block=True) |
|
573 | ar = view.execute("5", block=True) | |
574 | expected = [None] * len(view) |
|
574 | expected = [None] * len(view) | |
575 |
self.assertEqual(ar. |
|
575 | self.assertEqual(ar.execute_result, expected) | |
576 |
|
576 | |||
577 | def test_execute_magic(self): |
|
577 | def test_execute_magic(self): | |
578 | """execute accepts IPython commands""" |
|
578 | """execute accepts IPython commands""" |
@@ -1,5 +1,6 | |||||
1 | # Standard library imports |
|
1 | # Standard library imports | |
2 | import re |
|
2 | import re | |
|
3 | import textwrap | |||
3 | from unicodedata import category |
|
4 | from unicodedata import category | |
4 |
|
5 | |||
5 | # System library imports |
|
6 | # System library imports | |
@@ -122,21 +123,15 class CallTipWidget(QtGui.QLabel): | |||||
122 | # 'CallTipWidget' interface |
|
123 | # 'CallTipWidget' interface | |
123 | #-------------------------------------------------------------------------- |
|
124 | #-------------------------------------------------------------------------- | |
124 |
|
125 | |||
125 |
def show_ |
|
126 | def show_inspect_data(self, content, maxlines=20): | |
126 | """ Attempts to show the specified call line and docstring at the |
|
127 | """Show inspection data as a tooltip""" | |
127 | current cursor location. The docstring is possibly truncated for |
|
128 | data = content.get('data', {}) | |
128 | length. |
|
129 | text = data.get('text/plain', '') | |
129 | """ |
|
130 | match = re.match("(?:[^\n]*\n){%i}" % maxlines, text) | |
130 | if doc: |
|
|||
131 | match = re.match("(?:[^\n]*\n){%i}" % maxlines, doc) |
|
|||
132 |
|
|
131 | if match: | |
133 |
|
|
132 | text = text[:match.end()] + '\n[Documentation continues...]' | |
134 | else: |
|
|||
135 | doc = '' |
|
|||
136 |
|
133 | |||
137 | if call_line: |
|
134 | return self.show_tip(self._format_tooltip(text)) | |
138 | doc = '\n\n'.join([call_line, doc]) |
|
|||
139 | return self.show_tip(self._format_tooltip(doc)) |
|
|||
140 |
|
135 | |||
141 | def show_tip(self, tip): |
|
136 | def show_tip(self, tip): | |
142 | """ Attempts to show the specified tip at the current cursor location. |
|
137 | """ Attempts to show the specified tip at the current cursor location. | |
@@ -248,7 +243,7 class CallTipWidget(QtGui.QLabel): | |||||
248 | self._hide_timer.start(300, self) |
|
243 | self._hide_timer.start(300, self) | |
249 |
|
244 | |||
250 | def _format_tooltip(self,doc): |
|
245 | def _format_tooltip(self, doc): | |
251 | import textwrap |
|
246 | doc = re.sub(r'\033\[(\d|;)+?m', '', doc) | |
252 |
|
247 | |||
253 | # make sure a long argument list does not make |
|
248 | # make sure a long argument list does not make | |
254 | # the first row overflow the width of the actual tip body |
|
249 | # the first row overflow the width of the actual tip body |
@@ -1585,6 +1585,15 class ConsoleWidget(MetaQObjectHasTraits('NewBase', (LoggingConfigurable, QtGui. | |||||
1585 | text = self._get_block_plain_text(cursor.block()) |
|
1585 | text = self._get_block_plain_text(cursor.block()) | |
1586 | return text[len(prompt):] |
|
1586 | return text[len(prompt):] | |
1587 |
|
1587 | |||
|
1588 | def _get_input_buffer_cursor_pos(self): | |||
|
1589 | """Return the cursor position within the input buffer.""" | |||
|
1590 | cursor = self._control.textCursor() | |||
|
1591 | cursor.setPosition(self._prompt_pos, QtGui.QTextCursor.KeepAnchor) | |||
|
1592 | input_buffer = cursor.selection().toPlainText() | |||
|
1593 | ||||
|
1594 | # Don't count continuation prompts | |||
|
1595 | return len(input_buffer.replace('\n' + self._continuation_prompt, '\n')) | |||
|
1596 | ||||
1588 | def _get_input_buffer_cursor_prompt(self): |
|
1597 | def _get_input_buffer_cursor_prompt(self): | |
1589 | """ Returns the (plain text) prompt for line of the input buffer that |
|
1598 | """ Returns the (plain text) prompt for line of the input buffer that | |
1590 | contains the cursor, or None if there is no such line. |
|
1599 | contains the cursor, or None if there is no such line. |
@@ -1,17 +1,19 | |||||
|
1 | """Frontend widget for the Qt Console""" | |||
|
2 | ||||
|
3 | # Copyright (c) IPython Development Team. | |||
|
4 | # Distributed under the terms of the Modified BSD License. | |||
|
5 | ||||
1 | from __future__ import print_function |
|
6 | from __future__ import print_function | |
2 |
|
7 | |||
3 | # Standard library imports |
|
|||
4 | from collections import namedtuple |
|
8 | from collections import namedtuple | |
5 | import sys |
|
9 | import sys | |
6 | import uuid |
|
10 | import uuid | |
7 |
|
11 | |||
8 | # System library imports |
|
|||
9 | from IPython.external import qt |
|
12 | from IPython.external import qt | |
10 | from IPython.external.qt import QtCore, QtGui |
|
13 | from IPython.external.qt import QtCore, QtGui | |
11 | from IPython.utils import py3compat |
|
14 | from IPython.utils import py3compat | |
12 | from IPython.utils.importstring import import_item |
|
15 | from IPython.utils.importstring import import_item | |
13 |
|
16 | |||
14 | # Local imports |
|
|||
15 | from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter |
|
17 | from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter | |
16 | from IPython.core.inputtransformer import classic_prompt |
|
18 | from IPython.core.inputtransformer import classic_prompt | |
17 | from IPython.core.oinspect import call_tip |
|
19 | from IPython.core.oinspect import call_tip | |
@@ -80,6 +82,7 class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin): | |||||
80 |
|
82 | |||
81 | # The text to show when the kernel is (re)started. |
|
83 | # The text to show when the kernel is (re)started. | |
82 | banner = Unicode(config=True) |
|
84 | banner = Unicode(config=True) | |
|
85 | kernel_banner = Unicode() | |||
83 |
|
86 | |||
84 | # An option and corresponding signal for overriding the default kernel |
|
87 | # An option and corresponding signal for overriding the default kernel | |
85 | # interrupt behavior. |
|
88 | # interrupt behavior. | |
@@ -502,34 +505,21 class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin): | |||||
502 | self._kernel_restarted_message(died=died) |
|
505 | self._kernel_restarted_message(died=died) | |
503 | self.reset() |
|
506 | self.reset() | |
504 |
|
507 | |||
505 |
def _handle_ |
|
508 | def _handle_inspect_reply(self, rep): | |
506 |
""" |
|
509 | """Handle replies for call tips.""" | |
507 | """ |
|
|||
508 | self.log.debug("oinfo: %s", rep.get('content', '')) |
|
510 | self.log.debug("oinfo: %s", rep.get('content', '')) | |
509 | cursor = self._get_cursor() |
|
511 | cursor = self._get_cursor() | |
510 | info = self._request_info.get('call_tip') |
|
512 | info = self._request_info.get('call_tip') | |
511 | if info and info.id == rep['parent_header']['msg_id'] and \ |
|
513 | if info and info.id == rep['parent_header']['msg_id'] and \ | |
512 | info.pos == cursor.position(): |
|
514 | info.pos == cursor.position(): | |
513 | # Get the information for a call tip. For now we format the call |
|
|||
514 | # line as string, later we can pass False to format_call and |
|
|||
515 | # syntax-highlight it ourselves for nicer formatting in the |
|
|||
516 | # calltip. |
|
|||
517 | content = rep['content'] |
|
515 | content = rep['content'] | |
518 | # if this is from pykernel, 'docstring' will be the only key |
|
516 | if content.get('status') == 'ok': | |
519 | if content.get('ismagic', False): |
|
517 | self._call_tip_widget.show_inspect_data(content) | |
520 | # Don't generate a call-tip for magics. Ideally, we should |
|
|||
521 | # generate a tooltip, but not on ( like we do for actual |
|
|||
522 | # callables. |
|
|||
523 | call_info, doc = None, None |
|
|||
524 | else: |
|
|||
525 | call_info, doc = call_tip(content, format_call=True) |
|
|||
526 | if call_info or doc: |
|
|||
527 | self._call_tip_widget.show_call_info(call_info, doc) |
|
|||
528 |
|
518 | |||
529 |
def _handle_ |
|
519 | def _handle_execute_result(self, msg): | |
530 | """ Handle display hook output. |
|
520 | """ Handle display hook output. | |
531 | """ |
|
521 | """ | |
532 |
self.log.debug(" |
|
522 | self.log.debug("execute_result: %s", msg.get('content', '')) | |
533 | if not self._hidden and self._is_from_this_session(msg): |
|
523 | if not self._hidden and self._is_from_this_session(msg): | |
534 | self.flush_clearoutput() |
|
524 | self.flush_clearoutput() | |
535 | text = msg['content']['data'] |
|
525 | text = msg['content']['data'] | |
@@ -637,6 +627,9 class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin): | |||||
637 | if clear: |
|
627 | if clear: | |
638 | self._control.clear() |
|
628 | self._control.clear() | |
639 | self._append_plain_text(self.banner) |
|
629 | self._append_plain_text(self.banner) | |
|
630 | if self.kernel_banner: | |||
|
631 | self._append_plain_text(self.kernel_banner) | |||
|
632 | ||||
640 | # update output marker for stdout/stderr, so that startup |
|
633 | # update output marker for stdout/stderr, so that startup | |
641 | # messages appear after banner: |
|
634 | # messages appear after banner: | |
642 | self._append_before_prompt_pos = self._get_cursor().position() |
|
635 | self._append_before_prompt_pos = self._get_cursor().position() | |
@@ -725,17 +718,10 class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin): | |||||
725 | # Decide if it makes sense to show a call tip |
|
718 | # Decide if it makes sense to show a call tip | |
726 | if not self.enable_calltips: |
|
719 | if not self.enable_calltips: | |
727 | return False |
|
720 | return False | |
728 | cursor = self._get_cursor() |
|
721 | cursor_pos = self._get_input_buffer_cursor_pos() | |
729 | cursor.movePosition(QtGui.QTextCursor.Left) |
|
722 | code = self.input_buffer | |
730 | if cursor.document().characterAt(cursor.position()) != '(': |
|
|||
731 | return False |
|
|||
732 | context = self._get_context(cursor) |
|
|||
733 | if not context: |
|
|||
734 | return False |
|
|||
735 |
|
||||
736 | # Send the metadata request to the kernel |
|
723 | # Send the metadata request to the kernel | |
737 | name = '.'.join(context) |
|
724 | msg_id = self.kernel_client.inspect(code, cursor_pos) | |
738 | msg_id = self.kernel_client.object_info(name) |
|
|||
739 | pos = self._get_cursor().position() |
|
725 | pos = self._get_cursor().position() | |
740 | self._request_info['call_tip'] = self._CallTipRequest(msg_id, pos) |
|
726 | self._request_info['call_tip'] = self._CallTipRequest(msg_id, pos) | |
741 | return True |
|
727 | return True | |
@@ -747,10 +733,9 class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin): | |||||
747 | if context: |
|
733 | if context: | |
748 | # Send the completion request to the kernel |
|
734 | # Send the completion request to the kernel | |
749 | msg_id = self.kernel_client.complete( |
|
735 | msg_id = self.kernel_client.complete( | |
750 | '.'.join(context), # text |
|
736 | code=self.input_buffer, | |
751 |
self._get_input_buffer_cursor_ |
|
737 | cursor_pos=self._get_input_buffer_cursor_pos(), | |
752 | self._get_input_buffer_cursor_column(), # cursor_pos |
|
738 | ) | |
753 | self.input_buffer) # block |
|
|||
754 | pos = self._get_cursor().position() |
|
739 | pos = self._get_cursor().position() | |
755 | info = self._CompletionRequest(msg_id, pos) |
|
740 | info = self._CompletionRequest(msg_id, pos) | |
756 | self._request_info['complete'] = info |
|
741 | self._request_info['complete'] = info |
@@ -3,11 +3,9 | |||||
3 | This supports the additional functionality provided by the IPython kernel. |
|
3 | This supports the additional functionality provided by the IPython kernel. | |
4 | """ |
|
4 | """ | |
5 |
|
5 | |||
6 | #----------------------------------------------------------------------------- |
|
6 | # Copyright (c) IPython Development Team. | |
7 | # Imports |
|
7 | # Distributed under the terms of the Modified BSD License. | |
8 | #----------------------------------------------------------------------------- |
|
|||
9 |
|
8 | |||
10 | # Standard library imports |
|
|||
11 | from collections import namedtuple |
|
9 | from collections import namedtuple | |
12 | import os.path |
|
10 | import os.path | |
13 | import re |
|
11 | import re | |
@@ -16,11 +14,10 import sys | |||||
16 | import time |
|
14 | import time | |
17 | from textwrap import dedent |
|
15 | from textwrap import dedent | |
18 |
|
16 | |||
19 | # System library imports |
|
|||
20 | from IPython.external.qt import QtCore, QtGui |
|
17 | from IPython.external.qt import QtCore, QtGui | |
21 |
|
18 | |||
22 | # Local imports |
|
|||
23 | from IPython.core.inputsplitter import IPythonInputSplitter |
|
19 | from IPython.core.inputsplitter import IPythonInputSplitter | |
|
20 | from IPython.core.release import version | |||
24 | from IPython.core.inputtransformer import ipy_prompt |
|
21 | from IPython.core.inputtransformer import ipy_prompt | |
25 | from IPython.utils.traitlets import Bool, Unicode |
|
22 | from IPython.utils.traitlets import Bool, Unicode | |
26 | from .frontend_widget import FrontendWidget |
|
23 | from .frontend_widget import FrontendWidget | |
@@ -111,6 +108,7 class IPythonWidget(FrontendWidget): | |||||
111 | _payload_source_next_input = 'set_next_input' |
|
108 | _payload_source_next_input = 'set_next_input' | |
112 | _payload_source_page = 'page' |
|
109 | _payload_source_page = 'page' | |
113 | _retrying_history_request = False |
|
110 | _retrying_history_request = False | |
|
111 | _starting = False | |||
114 |
|
112 | |||
115 | #--------------------------------------------------------------------------- |
|
113 | #--------------------------------------------------------------------------- | |
116 | # 'object' interface |
|
114 | # 'object' interface | |
@@ -148,22 +146,12 class IPythonWidget(FrontendWidget): | |||||
148 | info = self._request_info.get('complete') |
|
146 | info = self._request_info.get('complete') | |
149 | if info and info.id == rep['parent_header']['msg_id'] and \ |
|
147 | if info and info.id == rep['parent_header']['msg_id'] and \ | |
150 | info.pos == cursor.position(): |
|
148 | info.pos == cursor.position(): | |
151 |
|
|
149 | content = rep['content'] | |
152 |
|
|
150 | matches = content['matches'] | |
153 | offset = len(text) |
|
151 | start = content['cursor_start'] | |
154 |
|
152 | end = content['cursor_end'] | ||
155 | # Clean up matches with period and path separators if the matched |
|
|||
156 | # text has not been transformed. This is done by truncating all |
|
|||
157 | # but the last component and then suitably decreasing the offset |
|
|||
158 | # between the current cursor position and the start of completion. |
|
|||
159 | if len(matches) > 1 and matches[0][:offset] == text: |
|
|||
160 | parts = re.split(r'[./\\]', text) |
|
|||
161 | sep_count = len(parts) - 1 |
|
|||
162 | if sep_count: |
|
|||
163 | chop_length = sum(map(len, parts[:sep_count])) + sep_count |
|
|||
164 | matches = [ match[chop_length:] for match in matches ] |
|
|||
165 | offset -= chop_length |
|
|||
166 |
|
153 | |||
|
154 | offset = end - start | |||
167 | # Move the cursor to the start of the match and complete. |
|
155 | # Move the cursor to the start of the match and complete. | |
168 | cursor.movePosition(QtGui.QTextCursor.Left, n=offset) |
|
156 | cursor.movePosition(QtGui.QTextCursor.Left, n=offset) | |
169 | self._complete_with_items(cursor, matches) |
|
157 | self._complete_with_items(cursor, matches) | |
@@ -217,10 +205,10 class IPythonWidget(FrontendWidget): | |||||
217 | last_cell = cell |
|
205 | last_cell = cell | |
218 | self._set_history(items) |
|
206 | self._set_history(items) | |
219 |
|
207 | |||
220 |
def _handle_ |
|
208 | def _handle_execute_result(self, msg): | |
221 | """ Reimplemented for IPython-style "display hook". |
|
209 | """ Reimplemented for IPython-style "display hook". | |
222 | """ |
|
210 | """ | |
223 |
self.log.debug(" |
|
211 | self.log.debug("execute_result: %s", msg.get('content', '')) | |
224 | if not self._hidden and self._is_from_this_session(msg): |
|
212 | if not self._hidden and self._is_from_this_session(msg): | |
225 | self.flush_clearoutput() |
|
213 | self.flush_clearoutput() | |
226 | content = msg['content'] |
|
214 | content = msg['content'] | |
@@ -257,31 +245,28 class IPythonWidget(FrontendWidget): | |||||
257 | self._append_plain_text(u'\n', True) |
|
245 | self._append_plain_text(u'\n', True) | |
258 |
|
246 | |||
259 | def _handle_kernel_info_reply(self, rep): |
|
247 | def _handle_kernel_info_reply(self, rep): | |
260 |
""" |
|
248 | """Handle kernel info replies.""" | |
261 | """ |
|
249 | content = rep['content'] | |
262 | if not self._guiref_loaded: |
|
250 | if not self._guiref_loaded: | |
263 |
if |
|
251 | if content.get('language') == 'python': | |
264 | self._load_guiref_magic() |
|
252 | self._load_guiref_magic() | |
265 | self._guiref_loaded = True |
|
253 | self._guiref_loaded = True | |
266 |
|
254 | |||
267 | def _started_channels(self): |
|
255 | self.kernel_banner = content.get('banner', '') | |
268 | """Reimplemented to make a history request and load %guiref.""" |
|
256 | if self._starting: | |
|
257 | # finish handling started channels | |||
|
258 | self._starting = False | |||
269 | super(IPythonWidget, self)._started_channels() |
|
259 | super(IPythonWidget, self)._started_channels() | |
270 |
|
260 | |||
|
261 | def _started_channels(self): | |||
|
262 | """Reimplemented to make a history request and load %guiref.""" | |||
|
263 | self._starting = True | |||
271 | # The reply will trigger %guiref load provided language=='python' |
|
264 | # The reply will trigger %guiref load provided language=='python' | |
272 | self.kernel_client.kernel_info() |
|
265 | self.kernel_client.kernel_info() | |
273 |
|
266 | |||
274 | self.kernel_client.shell_channel.history(hist_access_type='tail', |
|
267 | self.kernel_client.shell_channel.history(hist_access_type='tail', | |
275 | n=1000) |
|
268 | n=1000) | |
276 |
|
269 | |||
277 | def _started_kernel(self): |
|
|||
278 | """Load %guiref when the kernel starts (if channels are also started). |
|
|||
279 |
|
||||
280 | Principally triggered by kernel restart. |
|
|||
281 | """ |
|
|||
282 | if self.kernel_client.shell_channel is not None: |
|
|||
283 | self._load_guiref_magic() |
|
|||
284 |
|
||||
285 | def _load_guiref_magic(self): |
|
270 | def _load_guiref_magic(self): | |
286 | """Load %guiref magic.""" |
|
271 | """Load %guiref magic.""" | |
287 | self.kernel_client.shell_channel.execute('\n'.join([ |
|
272 | self.kernel_client.shell_channel.execute('\n'.join([ | |
@@ -331,24 +316,6 class IPythonWidget(FrontendWidget): | |||||
331 | # 'FrontendWidget' protected interface |
|
316 | # 'FrontendWidget' protected interface | |
332 | #--------------------------------------------------------------------------- |
|
317 | #--------------------------------------------------------------------------- | |
333 |
|
318 | |||
334 | def _complete(self): |
|
|||
335 | """ Reimplemented to support IPython's improved completion machinery. |
|
|||
336 | """ |
|
|||
337 | # We let the kernel split the input line, so we *always* send an empty |
|
|||
338 | # text field. Readline-based frontends do get a real text field which |
|
|||
339 | # they can use. |
|
|||
340 | text = '' |
|
|||
341 |
|
||||
342 | # Send the completion request to the kernel |
|
|||
343 | msg_id = self.kernel_client.shell_channel.complete( |
|
|||
344 | text, # text |
|
|||
345 | self._get_input_buffer_cursor_line(), # line |
|
|||
346 | self._get_input_buffer_cursor_column(), # cursor_pos |
|
|||
347 | self.input_buffer) # block |
|
|||
348 | pos = self._get_cursor().position() |
|
|||
349 | info = self._CompletionRequest(msg_id, pos) |
|
|||
350 | self._request_info['complete'] = info |
|
|||
351 |
|
||||
352 | def _process_execute_error(self, msg): |
|
319 | def _process_execute_error(self, msg): | |
353 | """ Reimplemented for IPython-style traceback formatting. |
|
320 | """ Reimplemented for IPython-style traceback formatting. | |
354 | """ |
|
321 | """ | |
@@ -555,10 +522,11 class IPythonWidget(FrontendWidget): | |||||
555 | # Since the plain text widget supports only a very small subset of HTML |
|
522 | # Since the plain text widget supports only a very small subset of HTML | |
556 | # and we have no control over the HTML source, we only page HTML |
|
523 | # and we have no control over the HTML source, we only page HTML | |
557 | # payloads in the rich text widget. |
|
524 | # payloads in the rich text widget. | |
558 | if item['html'] and self.kind == 'rich': |
|
525 | data = item['data'] | |
559 | self._page(item['html'], html=True) |
|
526 | if 'text/html' in data and self.kind == 'rich': | |
|
527 | self._page(data['text/html'], html=True) | |||
560 | else: |
|
528 | else: | |
561 |
self._page( |
|
529 | self._page(data['text/plain'], html=False) | |
562 |
|
530 | |||
563 | #------ Trait change handlers -------------------------------------------- |
|
531 | #------ Trait change handlers -------------------------------------------- | |
564 |
|
532 | |||
@@ -590,5 +558,4 class IPythonWidget(FrontendWidget): | |||||
590 | #------ Trait default initializers ----------------------------------------- |
|
558 | #------ Trait default initializers ----------------------------------------- | |
591 |
|
559 | |||
592 | def _banner_default(self): |
|
560 | def _banner_default(self): | |
593 | from IPython.core.usage import default_gui_banner |
|
561 | return "IPython QtConsole {version}\n".format(version=version) | |
594 | return default_gui_banner |
|
@@ -98,12 +98,12 class RichIPythonWidget(IPythonWidget): | |||||
98 |
|
98 | |||
99 | Shared code for some the following if statement |
|
99 | Shared code for some the following if statement | |
100 | """ |
|
100 | """ | |
101 |
self.log.debug(" |
|
101 | self.log.debug("execute_result: %s", msg.get('content', '')) | |
102 | self._append_plain_text(self.output_sep, True) |
|
102 | self._append_plain_text(self.output_sep, True) | |
103 | self._append_html(self._make_out_prompt(prompt_number), True) |
|
103 | self._append_html(self._make_out_prompt(prompt_number), True) | |
104 | self._append_plain_text('\n', True) |
|
104 | self._append_plain_text('\n', True) | |
105 |
|
105 | |||
106 |
def _handle_ |
|
106 | def _handle_execute_result(self, msg): | |
107 | """ Overridden to handle rich data types, like SVG. |
|
107 | """ Overridden to handle rich data types, like SVG. | |
108 | """ |
|
108 | """ | |
109 | if not self._hidden and self._is_from_this_session(msg): |
|
109 | if not self._hidden and self._is_from_this_session(msg): | |
@@ -128,7 +128,7 class RichIPythonWidget(IPythonWidget): | |||||
128 | self._append_html(self.output_sep2, True) |
|
128 | self._append_html(self.output_sep2, True) | |
129 | else: |
|
129 | else: | |
130 | # Default back to the plain text representation. |
|
130 | # Default back to the plain text representation. | |
131 |
return super(RichIPythonWidget, self)._handle_ |
|
131 | return super(RichIPythonWidget, self)._handle_execute_result(msg) | |
132 |
|
132 | |||
133 | def _handle_display_data(self, msg): |
|
133 | def _handle_display_data(self, msg): | |
134 | """ Overridden to handle rich data types, like SVG. |
|
134 | """ Overridden to handle rich data types, like SVG. |
@@ -1,10 +1,10 | |||||
1 |
""" |
|
1 | """Defines a KernelManager that provides signals and slots.""" | |
2 | """ |
|
2 | ||
|
3 | # Copyright (c) IPython Development Team. | |||
|
4 | # Distributed under the terms of the Modified BSD License. | |||
3 |
|
5 | |||
4 | # System library imports. |
|
|||
5 | from IPython.external.qt import QtCore |
|
6 | from IPython.external.qt import QtCore | |
6 |
|
7 | |||
7 | # IPython imports. |
|
|||
8 | from IPython.utils.traitlets import HasTraits, Type |
|
8 | from IPython.utils.traitlets import HasTraits, Type | |
9 | from .util import MetaQObjectHasTraits, SuperQObject |
|
9 | from .util import MetaQObjectHasTraits, SuperQObject | |
10 |
|
10 | |||
@@ -57,7 +57,7 class QtShellChannelMixin(ChannelQObject): | |||||
57 | # Emitted when a reply has been received for the corresponding request type. |
|
57 | # Emitted when a reply has been received for the corresponding request type. | |
58 | execute_reply = QtCore.Signal(object) |
|
58 | execute_reply = QtCore.Signal(object) | |
59 | complete_reply = QtCore.Signal(object) |
|
59 | complete_reply = QtCore.Signal(object) | |
60 |
|
|
60 | inspect_reply = QtCore.Signal(object) | |
61 | history_reply = QtCore.Signal(object) |
|
61 | history_reply = QtCore.Signal(object) | |
62 |
|
62 | |||
63 | #--------------------------------------------------------------------------- |
|
63 | #--------------------------------------------------------------------------- | |
@@ -85,14 +85,14 class QtIOPubChannelMixin(ChannelQObject): | |||||
85 | # Emitted when a message of type 'stream' is received. |
|
85 | # Emitted when a message of type 'stream' is received. | |
86 | stream_received = QtCore.Signal(object) |
|
86 | stream_received = QtCore.Signal(object) | |
87 |
|
87 | |||
88 |
# Emitted when a message of type ' |
|
88 | # Emitted when a message of type 'execute_input' is received. | |
89 |
|
|
89 | execute_input_received = QtCore.Signal(object) | |
90 |
|
90 | |||
91 |
# Emitted when a message of type ' |
|
91 | # Emitted when a message of type 'execute_result' is received. | |
92 |
|
|
92 | execute_result_received = QtCore.Signal(object) | |
93 |
|
93 | |||
94 |
# Emitted when a message of type ' |
|
94 | # Emitted when a message of type 'error' is received. | |
95 |
|
|
95 | error_received = QtCore.Signal(object) | |
96 |
|
96 | |||
97 | # Emitted when a message of type 'display_data' is received |
|
97 | # Emitted when a message of type 'display_data' is received | |
98 | display_data_received = QtCore.Signal(object) |
|
98 | display_data_received = QtCore.Signal(object) |
@@ -1,6 +1,9 | |||||
1 | """Adapt readline completer interface to make ZMQ request. |
|
|||
2 | """ |
|
|||
3 |
|
|
1 | # -*- coding: utf-8 -*- | |
|
2 | """Adapt readline completer interface to make ZMQ request.""" | |||
|
3 | ||||
|
4 | # Copyright (c) IPython Development Team. | |||
|
5 | # Distributed under the terms of the Modified BSD License. | |||
|
6 | ||||
4 | try: |
|
7 | try: | |
5 | from queue import Empty # Py 3 |
|
8 | from queue import Empty # Py 3 | |
6 | except ImportError: |
|
9 | except ImportError: | |
@@ -33,8 +36,10 class ZMQCompleter(IPCompleter): | |||||
33 |
|
36 | |||
34 | # send completion request to kernel |
|
37 | # send completion request to kernel | |
35 | # Give the kernel up to 0.5s to respond |
|
38 | # Give the kernel up to 0.5s to respond | |
36 |
msg_id = self.client.shell_channel.complete( |
|
39 | msg_id = self.client.shell_channel.complete( | |
37 | cursor_pos=cursor_pos) |
|
40 | code=line, | |
|
41 | cursor_pos=cursor_pos, | |||
|
42 | ) | |||
38 |
|
43 | |||
39 | msg = self.client.shell_channel.get_msg(timeout=self.timeout) |
|
44 | msg = self.client.shell_channel.get_msg(timeout=self.timeout) | |
40 | if msg['parent_header']['msg_id'] == msg_id: |
|
45 | if msg['parent_header']['msg_id'] == msg_id: |
@@ -1,27 +1,20 | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """terminal client to the IPython kernel |
|
2 | """terminal client to the IPython kernel""" | |
|
3 | ||||
|
4 | # Copyright (c) IPython Development Team. | |||
|
5 | # Distributed under the terms of the Modified BSD License. | |||
3 |
|
6 | |||
4 | """ |
|
|||
5 | #----------------------------------------------------------------------------- |
|
|||
6 | # Copyright (C) 2013 The IPython Development Team |
|
|||
7 | # |
|
|||
8 | # Distributed under the terms of the BSD License. The full license is in |
|
|||
9 | # the file COPYING, distributed as part of this software. |
|
|||
10 | #----------------------------------------------------------------------------- |
|
|||
11 |
|
||||
12 | #----------------------------------------------------------------------------- |
|
|||
13 | # Imports |
|
|||
14 | #----------------------------------------------------------------------------- |
|
|||
15 | from __future__ import print_function |
|
7 | from __future__ import print_function | |
16 |
|
8 | |||
|
9 | import base64 | |||
17 | import bdb |
|
10 | import bdb | |
18 | import signal |
|
11 | import signal | |
19 | import os |
|
12 | import os | |
20 | import sys |
|
13 | import sys | |
21 | import time |
|
14 | import time | |
22 | import subprocess |
|
15 | import subprocess | |
|
16 | from getpass import getpass | |||
23 | from io import BytesIO |
|
17 | from io import BytesIO | |
24 | import base64 |
|
|||
25 |
|
18 | |||
26 | try: |
|
19 | try: | |
27 | from queue import Empty # Py 3 |
|
20 | from queue import Empty # Py 3 | |
@@ -29,6 +22,7 except ImportError: | |||||
29 | from Queue import Empty # Py 2 |
|
22 | from Queue import Empty # Py 2 | |
30 |
|
23 | |||
31 | from IPython.core import page |
|
24 | from IPython.core import page | |
|
25 | from IPython.core import release | |||
32 | from IPython.utils.warn import warn, error |
|
26 | from IPython.utils.warn import warn, error | |
33 | from IPython.utils import io |
|
27 | from IPython.utils import io | |
34 | from IPython.utils.py3compat import string_types, input |
|
28 | from IPython.utils.py3compat import string_types, input | |
@@ -44,6 +38,7 class ZMQTerminalInteractiveShell(TerminalInteractiveShell): | |||||
44 | _executing = False |
|
38 | _executing = False | |
45 | _execution_state = Unicode('') |
|
39 | _execution_state = Unicode('') | |
46 | _pending_clearoutput = False |
|
40 | _pending_clearoutput = False | |
|
41 | kernel_banner = Unicode('') | |||
47 | kernel_timeout = Float(60, config=True, |
|
42 | kernel_timeout = Float(60, config=True, | |
48 | help="""Timeout for giving up on a kernel (in seconds). |
|
43 | help="""Timeout for giving up on a kernel (in seconds). | |
49 |
|
44 | |||
@@ -179,7 +174,7 class ZMQTerminalInteractiveShell(TerminalInteractiveShell): | |||||
179 | self._execution_state = "busy" |
|
174 | self._execution_state = "busy" | |
180 | while self._execution_state != 'idle' and self.client.is_alive(): |
|
175 | while self._execution_state != 'idle' and self.client.is_alive(): | |
181 | try: |
|
176 | try: | |
182 |
self.handle_ |
|
177 | self.handle_input_request(msg_id, timeout=0.05) | |
183 | except Empty: |
|
178 | except Empty: | |
184 | # display intermediate print statements, etc. |
|
179 | # display intermediate print statements, etc. | |
185 | self.handle_iopub(msg_id) |
|
180 | self.handle_iopub(msg_id) | |
@@ -211,11 +206,13 class ZMQTerminalInteractiveShell(TerminalInteractiveShell): | |||||
211 | self.write('Aborted\n') |
|
206 | self.write('Aborted\n') | |
212 | return |
|
207 | return | |
213 | elif status == 'ok': |
|
208 | elif status == 'ok': | |
214 |
# |
|
209 | # handle payloads | |
215 | for item in content["payload"]: |
|
210 | for item in content["payload"]: | |
216 |
|
|
211 | source = item['source'] | |
217 |
if |
|
212 | if source == 'page': | |
218 | page.page(text) |
|
213 | page.page(item['data']['text/plain']) | |
|
214 | elif source == 'set_next_input': | |||
|
215 | self.set_next_input(item['text']) | |||
219 |
|
216 | |||
220 | elif status == 'error': |
|
217 | elif status == 'error': | |
221 | for frame in content["traceback"]: |
|
218 | for frame in content["traceback"]: | |
@@ -228,7 +225,7 class ZMQTerminalInteractiveShell(TerminalInteractiveShell): | |||||
228 | """Process messages on the IOPub channel |
|
225 | """Process messages on the IOPub channel | |
229 |
|
226 | |||
230 | This method consumes and processes messages on the IOPub channel, |
|
227 | This method consumes and processes messages on the IOPub channel, | |
231 |
such as stdout, stderr, |
|
228 | such as stdout, stderr, execute_result and status. | |
232 |
|
229 | |||
233 | It only displays output that is caused by this session. |
|
230 | It only displays output that is caused by this session. | |
234 | """ |
|
231 | """ | |
@@ -254,7 +251,7 class ZMQTerminalInteractiveShell(TerminalInteractiveShell): | |||||
254 | print(sub_msg["content"]["data"], file=io.stderr, end="") |
|
251 | print(sub_msg["content"]["data"], file=io.stderr, end="") | |
255 | io.stderr.flush() |
|
252 | io.stderr.flush() | |
256 |
|
253 | |||
257 |
elif msg_type == ' |
|
254 | elif msg_type == 'execute_result': | |
258 | if self._pending_clearoutput: |
|
255 | if self._pending_clearoutput: | |
259 | print("\r", file=io.stdout, end="") |
|
256 | print("\r", file=io.stdout, end="") | |
260 | self._pending_clearoutput = False |
|
257 | self._pending_clearoutput = False | |
@@ -335,13 +332,13 class ZMQTerminalInteractiveShell(TerminalInteractiveShell): | |||||
335 | def handle_image_callable(self, data, mime): |
|
332 | def handle_image_callable(self, data, mime): | |
336 | self.callable_image_handler(data) |
|
333 | self.callable_image_handler(data) | |
337 |
|
334 | |||
338 |
def handle_ |
|
335 | def handle_input_request(self, msg_id, timeout=0.1): | |
339 | """ Method to capture raw_input |
|
336 | """ Method to capture raw_input | |
340 | """ |
|
337 | """ | |
341 |
|
|
338 | req = self.client.stdin_channel.get_msg(timeout=timeout) | |
342 | # in case any iopub came while we were waiting: |
|
339 | # in case any iopub came while we were waiting: | |
343 | self.handle_iopub(msg_id) |
|
340 | self.handle_iopub(msg_id) | |
344 |
if msg_id == |
|
341 | if msg_id == req["parent_header"].get("msg_id"): | |
345 | # wrap SIGINT handler |
|
342 | # wrap SIGINT handler | |
346 | real_handler = signal.getsignal(signal.SIGINT) |
|
343 | real_handler = signal.getsignal(signal.SIGINT) | |
347 | def double_int(sig,frame): |
|
344 | def double_int(sig,frame): | |
@@ -350,9 +347,10 class ZMQTerminalInteractiveShell(TerminalInteractiveShell): | |||||
350 | real_handler(sig,frame) |
|
347 | real_handler(sig,frame) | |
351 | raise KeyboardInterrupt |
|
348 | raise KeyboardInterrupt | |
352 | signal.signal(signal.SIGINT, double_int) |
|
349 | signal.signal(signal.SIGINT, double_int) | |
353 |
|
350 | content = req['content'] | ||
|
351 | read = getpass if content.get('password', False) else input | |||
354 | try: |
|
352 | try: | |
355 |
raw_data = |
|
353 | raw_data = read(content["prompt"]) | |
356 | except EOFError: |
|
354 | except EOFError: | |
357 | # turn EOFError into EOF character |
|
355 | # turn EOFError into EOF character | |
358 | raw_data = '\x04' |
|
356 | raw_data = '\x04' | |
@@ -381,6 +379,24 class ZMQTerminalInteractiveShell(TerminalInteractiveShell): | |||||
381 | # handling seems rather unpredictable... |
|
379 | # handling seems rather unpredictable... | |
382 | self.write("\nKeyboardInterrupt in interact()\n") |
|
380 | self.write("\nKeyboardInterrupt in interact()\n") | |
383 |
|
381 | |||
|
382 | def _banner1_default(self): | |||
|
383 | return "IPython Console {version}\n".format(version=release.version) | |||
|
384 | ||||
|
385 | def compute_banner(self): | |||
|
386 | super(ZMQTerminalInteractiveShell, self).compute_banner() | |||
|
387 | if self.client and not self.kernel_banner: | |||
|
388 | msg_id = self.client.kernel_info() | |||
|
389 | while True: | |||
|
390 | try: | |||
|
391 | reply = self.client.get_shell_msg(timeout=1) | |||
|
392 | except Empty: | |||
|
393 | break | |||
|
394 | else: | |||
|
395 | if reply['parent_header'].get('msg_id') == msg_id: | |||
|
396 | self.kernel_banner = reply['content'].get('banner', '') | |||
|
397 | break | |||
|
398 | self.banner += self.kernel_banner | |||
|
399 | ||||
384 | def wait_for_kernel(self, timeout=None): |
|
400 | def wait_for_kernel(self, timeout=None): | |
385 | """method to wait for a kernel to be ready""" |
|
401 | """method to wait for a kernel to be ready""" | |
386 | tic = time.time() |
|
402 | tic = time.time() |
@@ -75,24 +75,17 class InteractiveShellEmbed(TerminalInteractiveShell): | |||||
75 | # Like the base class display_banner is not configurable, but here it |
|
75 | # Like the base class display_banner is not configurable, but here it | |
76 | # is True by default. |
|
76 | # is True by default. | |
77 | display_banner = CBool(True) |
|
77 | display_banner = CBool(True) | |
|
78 | exit_msg = Unicode() | |||
78 |
|
79 | |||
79 | def __init__(self, config=None, ipython_dir=None, user_ns=None, |
|
|||
80 | user_module=None, custom_exceptions=((),None), |
|
|||
81 | usage=None, banner1=None, banner2=None, |
|
|||
82 | display_banner=None, exit_msg=u'', user_global_ns=None): |
|
|||
83 |
|
80 | |||
84 | if user_global_ns is not None: |
|
81 | def __init__(self, **kw): | |
|
82 | ||||
|
83 | ||||
|
84 | if kw.get('user_global_ns', None) is not None: | |||
85 | warnings.warn("user_global_ns has been replaced by user_module. The\ |
|
85 | warnings.warn("user_global_ns has been replaced by user_module. The\ | |
86 | parameter will be ignored.", DeprecationWarning) |
|
86 | parameter will be ignored.", DeprecationWarning) | |
87 |
|
87 | |||
88 | super(InteractiveShellEmbed,self).__init__( |
|
88 | super(InteractiveShellEmbed,self).__init__(**kw) | |
89 | config=config, ipython_dir=ipython_dir, user_ns=user_ns, |
|
|||
90 | user_module=user_module, custom_exceptions=custom_exceptions, |
|
|||
91 | usage=usage, banner1=banner1, banner2=banner2, |
|
|||
92 | display_banner=display_banner |
|
|||
93 | ) |
|
|||
94 |
|
||||
95 | self.exit_msg = exit_msg |
|
|||
96 |
|
89 | |||
97 | # don't use the ipython crash handler so that user exceptions aren't |
|
90 | # don't use the ipython crash handler so that user exceptions aren't | |
98 | # trapped |
|
91 | # trapped |
@@ -20,7 +20,7 import os | |||||
20 | import sys |
|
20 | import sys | |
21 |
|
21 | |||
22 | from IPython.core.error import TryNext, UsageError |
|
22 | from IPython.core.error import TryNext, UsageError | |
23 |
from IPython.core.usage import interactive_usage |
|
23 | from IPython.core.usage import interactive_usage | |
24 | from IPython.core.inputsplitter import IPythonInputSplitter |
|
24 | from IPython.core.inputsplitter import IPythonInputSplitter | |
25 | from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC |
|
25 | from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC | |
26 | from IPython.core.magic import Magics, magics_class, line_magic |
|
26 | from IPython.core.magic import Magics, magics_class, line_magic | |
@@ -258,13 +258,6 class TerminalInteractiveShell(InteractiveShell): | |||||
258 |
|
258 | |||
259 | autoedit_syntax = CBool(False, config=True, |
|
259 | autoedit_syntax = CBool(False, config=True, | |
260 | help="auto editing of files with syntax errors.") |
|
260 | help="auto editing of files with syntax errors.") | |
261 | banner = Unicode('') |
|
|||
262 | banner1 = Unicode(default_banner, config=True, |
|
|||
263 | help="""The part of the banner to be printed before the profile""" |
|
|||
264 | ) |
|
|||
265 | banner2 = Unicode('', config=True, |
|
|||
266 | help="""The part of the banner to be printed after the profile""" |
|
|||
267 | ) |
|
|||
268 | confirm_exit = CBool(True, config=True, |
|
261 | confirm_exit = CBool(True, config=True, | |
269 | help=""" |
|
262 | help=""" | |
270 | Set to confirm when you try to exit IPython with an EOF (Control-D |
|
263 | Set to confirm when you try to exit IPython with an EOF (Control-D | |
@@ -274,8 +267,7 class TerminalInteractiveShell(InteractiveShell): | |||||
274 | # This display_banner only controls whether or not self.show_banner() |
|
267 | # This display_banner only controls whether or not self.show_banner() | |
275 | # is called when mainloop/interact are called. The default is False |
|
268 | # is called when mainloop/interact are called. The default is False | |
276 | # because for the terminal based application, the banner behavior |
|
269 | # because for the terminal based application, the banner behavior | |
277 | # is controlled by Global.display_banner, which IPythonApp looks at |
|
270 | # is controlled by the application. | |
278 | # to determine if *it* should call show_banner() by hand or not. |
|
|||
279 | display_banner = CBool(False) # This isn't configurable! |
|
271 | display_banner = CBool(False) # This isn't configurable! | |
280 | embedded = CBool(False) |
|
272 | embedded = CBool(False) | |
281 | embedded_active = CBool(False) |
|
273 | embedded_active = CBool(False) | |
@@ -300,6 +292,7 class TerminalInteractiveShell(InteractiveShell): | |||||
300 | term_title = CBool(False, config=True, |
|
292 | term_title = CBool(False, config=True, | |
301 | help="Enable auto setting the terminal title." |
|
293 | help="Enable auto setting the terminal title." | |
302 | ) |
|
294 | ) | |
|
295 | usage = Unicode(interactive_usage) | |||
303 |
|
296 | |||
304 | # This `using_paste_magics` is used to detect whether the code is being |
|
297 | # This `using_paste_magics` is used to detect whether the code is being | |
305 | # executed via paste magics functions |
|
298 | # executed via paste magics functions | |
@@ -317,23 +310,7 class TerminalInteractiveShell(InteractiveShell): | |||||
317 | except ValueError as e: |
|
310 | except ValueError as e: | |
318 | raise UsageError("%s" % e) |
|
311 | raise UsageError("%s" % e) | |
319 |
|
312 | |||
320 | def __init__(self, config=None, ipython_dir=None, profile_dir=None, |
|
313 | system = InteractiveShell.system_raw | |
321 | user_ns=None, user_module=None, custom_exceptions=((),None), |
|
|||
322 | usage=None, banner1=None, banner2=None, display_banner=None, |
|
|||
323 | **kwargs): |
|
|||
324 |
|
||||
325 | super(TerminalInteractiveShell, self).__init__( |
|
|||
326 | config=config, ipython_dir=ipython_dir, profile_dir=profile_dir, user_ns=user_ns, |
|
|||
327 | user_module=user_module, custom_exceptions=custom_exceptions, |
|
|||
328 | **kwargs |
|
|||
329 | ) |
|
|||
330 | # use os.system instead of utils.process.system by default, |
|
|||
331 | # because piped system doesn't make sense in the Terminal: |
|
|||
332 | self.system = self.system_raw |
|
|||
333 |
|
||||
334 | self.init_term_title() |
|
|||
335 | self.init_usage(usage) |
|
|||
336 | self.init_banner(banner1, banner2, display_banner) |
|
|||
337 |
|
314 | |||
338 | #------------------------------------------------------------------------- |
|
315 | #------------------------------------------------------------------------- | |
339 | # Overrides of init stages |
|
316 | # Overrides of init stages | |
@@ -356,6 +333,9 class TerminalInteractiveShell(InteractiveShell): | |||||
356 | num_lines_bot = self.separate_in.count('\n')+1 |
|
333 | num_lines_bot = self.separate_in.count('\n')+1 | |
357 | return self.screen_length - num_lines_bot |
|
334 | return self.screen_length - num_lines_bot | |
358 |
|
335 | |||
|
336 | def _term_title_changed(self, name, new_value): | |||
|
337 | self.init_term_title() | |||
|
338 | ||||
359 | def init_term_title(self): |
|
339 | def init_term_title(self): | |
360 | # Enable or disable the terminal title. |
|
340 | # Enable or disable the terminal title. | |
361 | if self.term_title: |
|
341 | if self.term_title: | |
@@ -386,46 +366,6 class TerminalInteractiveShell(InteractiveShell): | |||||
386 | self.alias_manager.soft_define_alias(name, cmd) |
|
366 | self.alias_manager.soft_define_alias(name, cmd) | |
387 |
|
367 | |||
388 | #------------------------------------------------------------------------- |
|
368 | #------------------------------------------------------------------------- | |
389 | # Things related to the banner and usage |
|
|||
390 | #------------------------------------------------------------------------- |
|
|||
391 |
|
||||
392 | def _banner1_changed(self): |
|
|||
393 | self.compute_banner() |
|
|||
394 |
|
||||
395 | def _banner2_changed(self): |
|
|||
396 | self.compute_banner() |
|
|||
397 |
|
||||
398 | def _term_title_changed(self, name, new_value): |
|
|||
399 | self.init_term_title() |
|
|||
400 |
|
||||
401 | def init_banner(self, banner1, banner2, display_banner): |
|
|||
402 | if banner1 is not None: |
|
|||
403 | self.banner1 = banner1 |
|
|||
404 | if banner2 is not None: |
|
|||
405 | self.banner2 = banner2 |
|
|||
406 | if display_banner is not None: |
|
|||
407 | self.display_banner = display_banner |
|
|||
408 | self.compute_banner() |
|
|||
409 |
|
||||
410 | def show_banner(self, banner=None): |
|
|||
411 | if banner is None: |
|
|||
412 | banner = self.banner |
|
|||
413 | self.write(banner) |
|
|||
414 |
|
||||
415 | def compute_banner(self): |
|
|||
416 | self.banner = self.banner1 |
|
|||
417 | if self.profile and self.profile != 'default': |
|
|||
418 | self.banner += '\nIPython profile: %s\n' % self.profile |
|
|||
419 | if self.banner2: |
|
|||
420 | self.banner += '\n' + self.banner2 |
|
|||
421 |
|
||||
422 | def init_usage(self, usage=None): |
|
|||
423 | if usage is None: |
|
|||
424 | self.usage = interactive_usage |
|
|||
425 | else: |
|
|||
426 | self.usage = usage |
|
|||
427 |
|
||||
428 | #------------------------------------------------------------------------- |
|
|||
429 | # Mainloop and code execution logic |
|
369 | # Mainloop and code execution logic | |
430 | #------------------------------------------------------------------------- |
|
370 | #------------------------------------------------------------------------- | |
431 |
|
371 |
@@ -1,19 +1,10 | |||||
1 | # encoding: utf-8 |
|
1 | # encoding: utf-8 | |
2 | """ |
|
2 | """IO capturing utilities.""" | |
3 | IO capturing utilities. |
|
|||
4 | """ |
|
|||
5 |
|
3 | |||
6 | #----------------------------------------------------------------------------- |
|
4 | # Copyright (c) IPython Development Team. | |
7 | # Copyright (C) 2013 The IPython Development Team |
|
5 | # Distributed under the terms of the Modified BSD License. | |
8 | # |
|
|||
9 | # Distributed under the terms of the BSD License. The full license is in |
|
|||
10 | # the file COPYING, distributed as part of this software. |
|
|||
11 | #----------------------------------------------------------------------------- |
|
|||
12 | from __future__ import print_function, absolute_import |
|
|||
13 |
|
6 | |||
14 | #----------------------------------------------------------------------------- |
|
7 | from __future__ import print_function, absolute_import | |
15 | # Imports |
|
|||
16 | #----------------------------------------------------------------------------- |
|
|||
17 |
|
8 | |||
18 | import sys |
|
9 | import sys | |
19 |
|
10 | |||
@@ -30,14 +21,13 else: | |||||
30 |
|
21 | |||
31 |
|
22 | |||
32 | class RichOutput(object): |
|
23 | class RichOutput(object): | |
33 |
def __init__(self |
|
24 | def __init__(self, data=None, metadata=None): | |
34 | self.source = source |
|
|||
35 | self.data = data or {} |
|
25 | self.data = data or {} | |
36 | self.metadata = metadata or {} |
|
26 | self.metadata = metadata or {} | |
37 |
|
27 | |||
38 | def display(self): |
|
28 | def display(self): | |
39 | from IPython.display import publish_display_data |
|
29 | from IPython.display import publish_display_data | |
40 |
publish_display_data( |
|
30 | publish_display_data(data=self.data, metadata=self.metadata) | |
41 |
|
31 | |||
42 | def _repr_mime_(self, mime): |
|
32 | def _repr_mime_(self, mime): | |
43 | if mime not in self.data: |
|
33 | if mime not in self.data: | |
@@ -118,7 +108,7 class CapturedIO(object): | |||||
118 | for o in c.outputs: |
|
108 | for o in c.outputs: | |
119 | display(o) |
|
109 | display(o) | |
120 | """ |
|
110 | """ | |
121 |
return [ RichOutput( |
|
111 | return [ RichOutput(d, md) for d, md in self._outputs ] | |
122 |
|
112 | |||
123 | def show(self): |
|
113 | def show(self): | |
124 | """write my output to sys.stdout/err as appropriate""" |
|
114 | """write my output to sys.stdout/err as appropriate""" | |
@@ -126,8 +116,8 class CapturedIO(object): | |||||
126 | sys.stderr.write(self.stderr) |
|
116 | sys.stderr.write(self.stderr) | |
127 | sys.stdout.flush() |
|
117 | sys.stdout.flush() | |
128 | sys.stderr.flush() |
|
118 | sys.stderr.flush() | |
129 |
for |
|
119 | for data, metadata in self._outputs: | |
130 |
RichOutput( |
|
120 | RichOutput(data, metadata).display() | |
131 |
|
121 | |||
132 | __call__ = show |
|
122 | __call__ = show | |
133 |
|
123 |
@@ -78,8 +78,7 def test_rich_output(): | |||||
78 | """test RichOutput basics""" |
|
78 | """test RichOutput basics""" | |
79 | data = basic_data |
|
79 | data = basic_data | |
80 | metadata = basic_metadata |
|
80 | metadata = basic_metadata | |
81 |
rich = capture.RichOutput( |
|
81 | rich = capture.RichOutput(data=data, metadata=metadata) | |
82 | yield nt.assert_equal, rich.source, "test" |
|
|||
83 | yield nt.assert_equal, rich._repr_html_(), data['text/html'] |
|
82 | yield nt.assert_equal, rich._repr_html_(), data['text/html'] | |
84 | yield nt.assert_equal, rich._repr_png_(), (data['image/png'], metadata['image/png']) |
|
83 | yield nt.assert_equal, rich._repr_png_(), (data['image/png'], metadata['image/png']) | |
85 | yield nt.assert_equal, rich._repr_latex_(), None |
|
84 | yield nt.assert_equal, rich._repr_latex_(), None | |
@@ -89,7 +88,7 def test_rich_output(): | |||||
89 | def test_rich_output_no_metadata(): |
|
88 | def test_rich_output_no_metadata(): | |
90 | """test RichOutput with no metadata""" |
|
89 | """test RichOutput with no metadata""" | |
91 | data = full_data |
|
90 | data = full_data | |
92 |
rich = capture.RichOutput( |
|
91 | rich = capture.RichOutput(data=data) | |
93 | for method, mime in _mime_map.items(): |
|
92 | for method, mime in _mime_map.items(): | |
94 | yield nt.assert_equal, getattr(rich, method)(), data[mime] |
|
93 | yield nt.assert_equal, getattr(rich, method)(), data[mime] | |
95 |
|
94 | |||
@@ -97,7 +96,7 def test_rich_output_metadata(): | |||||
97 | """test RichOutput with metadata""" |
|
96 | """test RichOutput with metadata""" | |
98 | data = full_data |
|
97 | data = full_data | |
99 | metadata = full_metadata |
|
98 | metadata = full_metadata | |
100 |
rich = capture.RichOutput( |
|
99 | rich = capture.RichOutput(data=data, metadata=metadata) | |
101 | for method, mime in _mime_map.items(): |
|
100 | for method, mime in _mime_map.items(): | |
102 | yield nt.assert_equal, getattr(rich, method)(), (data[mime], metadata[mime]) |
|
101 | yield nt.assert_equal, getattr(rich, method)(), (data[mime], metadata[mime]) | |
103 |
|
102 |
@@ -519,3 +519,16 ul.keywordmatches li.goodmatch a { | |||||
519 | div.figure { |
|
519 | div.figure { | |
520 | text-align: center; |
|
520 | text-align: center; | |
521 | } |
|
521 | } | |
|
522 | ||||
|
523 | div.versionchanged { | |||
|
524 | margin-left: 30px; | |||
|
525 | margin-right: 30px; | |||
|
526 | } | |||
|
527 | ||||
|
528 | span.versionmodified { | |||
|
529 | font-style: italic; | |||
|
530 | } | |||
|
531 | ||||
|
532 | pre { | |||
|
533 | white-space: pre-wrap; | |||
|
534 | } No newline at end of file |
@@ -20,6 +20,7 on the IPython GitHub wiki. | |||||
20 | :maxdepth: 1 |
|
20 | :maxdepth: 1 | |
21 |
|
21 | |||
22 | messaging |
|
22 | messaging | |
|
23 | execution | |||
23 | parallel_messages |
|
24 | parallel_messages | |
24 | parallel_connections |
|
25 | parallel_connections | |
25 | lexer |
|
26 | lexer |
This diff has been collapsed as it changes many lines, (552 lines changed) Show them Hide them | |||||
@@ -9,7 +9,7 Versioning | |||||
9 | ========== |
|
9 | ========== | |
10 |
|
10 | |||
11 | The IPython message specification is versioned independently of IPython. |
|
11 | The IPython message specification is versioned independently of IPython. | |
12 |
The current version of the specification is |
|
12 | The current version of the specification is 5.0. | |
13 |
|
13 | |||
14 |
|
14 | |||
15 | Introduction |
|
15 | Introduction | |
@@ -38,22 +38,13 The basic design is explained in the following diagram: | |||||
38 | A single kernel can be simultaneously connected to one or more frontends. The |
|
38 | A single kernel can be simultaneously connected to one or more frontends. The | |
39 | kernel has three sockets that serve the following functions: |
|
39 | kernel has three sockets that serve the following functions: | |
40 |
|
40 | |||
41 | 1. stdin: this ROUTER socket is connected to all frontends, and it allows |
|
41 | 1. Shell: this single ROUTER socket allows multiple incoming connections from | |
42 | the kernel to request input from the active frontend when :func:`raw_input` is called. |
|
|||
43 | The frontend that executed the code has a DEALER socket that acts as a 'virtual keyboard' |
|
|||
44 | for the kernel while this communication is happening (illustrated in the |
|
|||
45 | figure by the black outline around the central keyboard). In practice, |
|
|||
46 | frontends may display such kernel requests using a special input widget or |
|
|||
47 | otherwise indicating that the user is to type input for the kernel instead |
|
|||
48 | of normal commands in the frontend. |
|
|||
49 |
|
||||
50 | 2. Shell: this single ROUTER socket allows multiple incoming connections from |
|
|||
51 | frontends, and this is the socket where requests for code execution, object |
|
42 | frontends, and this is the socket where requests for code execution, object | |
52 | information, prompts, etc. are made to the kernel by any frontend. The |
|
43 | information, prompts, etc. are made to the kernel by any frontend. The | |
53 | communication on this socket is a sequence of request/reply actions from |
|
44 | communication on this socket is a sequence of request/reply actions from | |
54 | each frontend and the kernel. |
|
45 | each frontend and the kernel. | |
55 |
|
46 | |||
56 |
|
|
47 | 2. IOPub: this socket is the 'broadcast channel' where the kernel publishes all | |
57 | side effects (stdout, stderr, etc.) as well as the requests coming from any |
|
48 | side effects (stdout, stderr, etc.) as well as the requests coming from any | |
58 | client over the shell socket and its own requests on the stdin socket. There |
|
49 | client over the shell socket and its own requests on the stdin socket. There | |
59 | are a number of actions in Python which generate side effects: :func:`print` |
|
50 | are a number of actions in Python which generate side effects: :func:`print` | |
@@ -64,11 +55,23 kernel has three sockets that serve the following functions: | |||||
64 | about communications taking place with one client over the shell channel |
|
55 | about communications taking place with one client over the shell channel | |
65 | to be made available to all clients in a uniform manner. |
|
56 | to be made available to all clients in a uniform manner. | |
66 |
|
57 | |||
|
58 | 3. stdin: this ROUTER socket is connected to all frontends, and it allows | |||
|
59 | the kernel to request input from the active frontend when :func:`raw_input` is called. | |||
|
60 | The frontend that executed the code has a DEALER socket that acts as a 'virtual keyboard' | |||
|
61 | for the kernel while this communication is happening (illustrated in the | |||
|
62 | figure by the black outline around the central keyboard). In practice, | |||
|
63 | frontends may display such kernel requests using a special input widget or | |||
|
64 | otherwise indicating that the user is to type input for the kernel instead | |||
|
65 | of normal commands in the frontend. | |||
|
66 | ||||
67 | All messages are tagged with enough information (details below) for clients |
|
67 | All messages are tagged with enough information (details below) for clients | |
68 | to know which messages come from their own interaction with the kernel and |
|
68 | to know which messages come from their own interaction with the kernel and | |
69 | which ones are from other clients, so they can display each type |
|
69 | which ones are from other clients, so they can display each type | |
70 | appropriately. |
|
70 | appropriately. | |
71 |
|
71 | |||
|
72 | 4. Control: This channel is identical to Shell, but operates on a separate socket, | |||
|
73 | to allow important messages to avoid queueing behind execution requests (e.g. shutdown or abort). | |||
|
74 | ||||
72 | The actual format of the messages allowed on each of these channels is |
|
75 | The actual format of the messages allowed on each of these channels is | |
73 | specified below. Messages are dicts of dicts with string keys and values that |
|
76 | specified below. Messages are dicts of dicts with string keys and values that | |
74 | are reasonably representable in JSON. Our current implementation uses JSON |
|
77 | are reasonably representable in JSON. Our current implementation uses JSON | |
@@ -103,6 +106,8 A message is defined by the following four-dictionary structure:: | |||||
103 | 'session' : uuid, |
|
106 | 'session' : uuid, | |
104 | # All recognized message type strings are listed below. |
|
107 | # All recognized message type strings are listed below. | |
105 | 'msg_type' : str, |
|
108 | 'msg_type' : str, | |
|
109 | # the message protocol version | |||
|
110 | 'version' : '5.0', | |||
106 | }, |
|
111 | }, | |
107 |
|
112 | |||
108 | # In a chain of messages, the header from the parent is copied so that |
|
113 | # In a chain of messages, the header from the parent is copied so that | |
@@ -117,6 +122,10 A message is defined by the following four-dictionary structure:: | |||||
117 | 'content' : dict, |
|
122 | 'content' : dict, | |
118 | } |
|
123 | } | |
119 |
|
124 | |||
|
125 | .. versionchanged:: 5.0 | |||
|
126 | ||||
|
127 | ``version`` key added to the header. | |||
|
128 | ||||
120 | The Wire Protocol |
|
129 | The Wire Protocol | |
121 | ================= |
|
130 | ================= | |
122 |
|
131 | |||
@@ -151,14 +160,14 The front of the message is the ZeroMQ routing prefix, | |||||
151 | which can be zero or more socket identities. |
|
160 | which can be zero or more socket identities. | |
152 | This is every piece of the message prior to the delimiter key ``<IDS|MSG>``. |
|
161 | This is every piece of the message prior to the delimiter key ``<IDS|MSG>``. | |
153 | In the case of IOPub, there should be just one prefix component, |
|
162 | In the case of IOPub, there should be just one prefix component, | |
154 |
which is the topic for IOPub subscribers, e.g. `` |
|
163 | which is the topic for IOPub subscribers, e.g. ``execute_result``, ``display_data``. | |
155 |
|
164 | |||
156 | .. note:: |
|
165 | .. note:: | |
157 |
|
166 | |||
158 | In most cases, the IOPub topics are irrelevant and completely ignored, |
|
167 | In most cases, the IOPub topics are irrelevant and completely ignored, | |
159 | because frontends just subscribe to all topics. |
|
168 | because frontends just subscribe to all topics. | |
160 | The convention used in the IPython kernel is to use the msg_type as the topic, |
|
169 | The convention used in the IPython kernel is to use the msg_type as the topic, | |
161 |
and possibly extra information about the message, e.g. `` |
|
170 | and possibly extra information about the message, e.g. ``execute_result`` or ``stream.stdout`` | |
162 |
|
171 | |||
163 | After the delimiter is the `HMAC`_ signature of the message, used for authentication. |
|
172 | After the delimiter is the `HMAC`_ signature of the message, used for authentication. | |
164 | If authentication is disabled, this should be an empty string. |
|
173 | If authentication is disabled, this should be an empty string. | |
@@ -248,13 +257,11 Message type: ``execute_request``:: | |||||
248 | 'code' : str, |
|
257 | 'code' : str, | |
249 |
|
258 | |||
250 | # A boolean flag which, if True, signals the kernel to execute |
|
259 | # A boolean flag which, if True, signals the kernel to execute | |
251 |
# this code as quietly as possible. |
|
260 | # this code as quietly as possible. | |
252 | # will compile the code with 'exec' instead of 'single' (so |
|
261 | # silent=True forces store_history to be False, | |
253 | # sys.displayhook will not fire), forces store_history to be False, |
|
|||
254 | # and will *not*: |
|
262 | # and will *not*: | |
255 |
# - broadcast |
|
263 | # - broadcast output on the IOPUB channel | |
256 | # - do any logging |
|
264 | # - have an execute_result | |
257 | # |
|
|||
258 | # The default is False. |
|
265 | # The default is False. | |
259 | 'silent' : bool, |
|
266 | 'silent' : bool, | |
260 |
|
267 | |||
@@ -263,156 +270,67 Message type: ``execute_request``:: | |||||
263 | # is forced to be False. |
|
270 | # is forced to be False. | |
264 | 'store_history' : bool, |
|
271 | 'store_history' : bool, | |
265 |
|
272 | |||
266 | # A list of variable names from the user's namespace to be retrieved. |
|
273 | # A dict mapping names to expressions to be evaluated in the | |
267 | # What returns is a rich representation of each variable (dict keyed by name). |
|
274 | # user's dict. The rich display-data representation of each will be evaluated after execution. | |
268 | # See the display_data content for the structure of the representation data. |
|
275 | # See the display_data content for the structure of the representation data. | |
269 | 'user_variables' : list, |
|
|||
270 |
|
||||
271 | # Similarly, a dict mapping names to expressions to be evaluated in the |
|
|||
272 | # user's dict. |
|
|||
273 | 'user_expressions' : dict, |
|
276 | 'user_expressions' : dict, | |
274 |
|
277 | |||
275 |
# Some frontends |
|
278 | # Some frontends do not support stdin requests. | |
276 |
# raw_input is called from code executed from such a frontend, |
|
279 | # If raw_input is called from code executed from such a frontend, | |
277 | # StdinNotImplementedError will be raised. |
|
280 | # a StdinNotImplementedError will be raised. | |
278 | 'allow_stdin' : True, |
|
281 | 'allow_stdin' : True, | |
279 |
|
||||
280 | } |
|
282 | } | |
281 |
|
283 | |||
282 | The ``code`` field contains a single string (possibly multiline). The kernel |
|
284 | .. versionchanged:: 5.0 | |
283 | is responsible for splitting this into one or more independent execution blocks |
|
285 | ||
284 | and deciding whether to compile these in 'single' or 'exec' mode (see below for |
|
286 | ``user_variables`` removed, because it is redundant with user_expressions. | |
285 | detailed execution semantics). |
|
287 | ||
|
288 | The ``code`` field contains a single string (possibly multiline) to be executed. | |||
286 |
|
289 | |||
287 |
The ``user_`` field |
|
290 | The ``user_expressions`` field deserves a detailed explanation. In the past, IPython had | |
288 | the notion of a prompt string that allowed arbitrary code to be evaluated, and |
|
291 | the notion of a prompt string that allowed arbitrary code to be evaluated, and | |
289 | this was put to good use by many in creating prompts that displayed system |
|
292 | this was put to good use by many in creating prompts that displayed system | |
290 | status, path information, and even more esoteric uses like remote instrument |
|
293 | status, path information, and even more esoteric uses like remote instrument | |
291 | status acquired over the network. But now that IPython has a clean separation |
|
294 | status acquired over the network. But now that IPython has a clean separation | |
292 | between the kernel and the clients, the kernel has no prompt knowledge; prompts |
|
295 | between the kernel and the clients, the kernel has no prompt knowledge; prompts | |
293 |
are a frontend |
|
296 | are a frontend feature, and it should be even possible for different | |
294 | frontends to display different prompts while interacting with the same kernel. |
|
297 | frontends to display different prompts while interacting with the same kernel. | |
|
298 | ``user_expressions`` can be used to retrieve this information. | |||
295 |
|
299 | |||
296 | The kernel now provides the ability to retrieve data from the user's namespace |
|
300 | Any error in evaluating any expression in ``user_expressions`` will result in | |
297 | after the execution of the main ``code``, thanks to two fields in the |
|
301 | only that key containing a standard error message, of the form:: | |
298 | ``execute_request`` message: |
|
|||
299 |
|
|
302 | ||
300 | - ``user_variables``: If only variables from the user's namespace are needed, a |
|
303 | { | |
301 | list of variable names can be passed and a dict with these names as keys and |
|
304 | 'status' : 'error', | |
302 | their :func:`repr()` as values will be returned. |
|
305 | 'ename' : 'NameError', | |
303 |
|
306 | 'evalue' : 'foo', | ||
304 | - ``user_expressions``: For more complex expressions that require function |
|
307 | 'traceback' : ... | |
305 | evaluations, a dict can be provided with string keys and arbitrary python |
|
308 | } | |
306 | expressions as values. The return message will contain also a dict with the |
|
|||
307 | same keys and the :func:`repr()` of the evaluated expressions as value. |
|
|||
308 |
|
||||
309 | With this information, frontends can display any status information they wish |
|
|||
310 | in the form that best suits each frontend (a status line, a popup, inline for a |
|
|||
311 | terminal, etc). |
|
|||
312 |
|
|
309 | ||
313 | .. Note:: |
|
310 | .. Note:: | |
314 |
|
311 | |||
315 | In order to obtain the current execution counter for the purposes of |
|
312 | In order to obtain the current execution counter for the purposes of | |
316 |
displaying input prompts, frontends |
|
313 | displaying input prompts, frontends may make an execution request with an | |
317 | empty code string and ``silent=True``. |
|
314 | empty code string and ``silent=True``. | |
318 |
|
315 | |||
319 | Execution semantics |
|
|||
320 | ~~~~~~~~~~~~~~~~~~~ |
|
|||
321 |
|
||||
322 | When the silent flag is false, the execution of use code consists of the |
|
|||
323 | following phases (in silent mode, only the ``code`` field is executed): |
|
|||
324 |
|
||||
325 | 1. Run the ``pre_runcode_hook``. |
|
|||
326 |
|
||||
327 | 2. Execute the ``code`` field, see below for details. |
|
|||
328 |
|
||||
329 | 3. If #2 succeeds, compute ``user_variables`` and ``user_expressions`` are |
|
|||
330 | computed. This ensures that any error in the latter don't harm the main |
|
|||
331 | code execution. |
|
|||
332 |
|
||||
333 | 4. Call any method registered with :meth:`register_post_execute`. |
|
|||
334 |
|
||||
335 | .. warning:: |
|
|||
336 |
|
||||
337 | The API for running code before/after the main code block is likely to |
|
|||
338 | change soon. Both the ``pre_runcode_hook`` and the |
|
|||
339 | :meth:`register_post_execute` are susceptible to modification, as we find a |
|
|||
340 | consistent model for both. |
|
|||
341 |
|
||||
342 | To understand how the ``code`` field is executed, one must know that Python |
|
|||
343 | code can be compiled in one of three modes (controlled by the ``mode`` argument |
|
|||
344 | to the :func:`compile` builtin): |
|
|||
345 |
|
||||
346 | *single* |
|
|||
347 | Valid for a single interactive statement (though the source can contain |
|
|||
348 | multiple lines, such as a for loop). When compiled in this mode, the |
|
|||
349 | generated bytecode contains special instructions that trigger the calling of |
|
|||
350 | :func:`sys.displayhook` for any expression in the block that returns a value. |
|
|||
351 | This means that a single statement can actually produce multiple calls to |
|
|||
352 | :func:`sys.displayhook`, if for example it contains a loop where each |
|
|||
353 | iteration computes an unassigned expression would generate 10 calls:: |
|
|||
354 |
|
||||
355 | for i in range(10): |
|
|||
356 | i**2 |
|
|||
357 |
|
||||
358 | *exec* |
|
|||
359 | An arbitrary amount of source code, this is how modules are compiled. |
|
|||
360 | :func:`sys.displayhook` is *never* implicitly called. |
|
|||
361 |
|
||||
362 | *eval* |
|
|||
363 | A single expression that returns a value. :func:`sys.displayhook` is *never* |
|
|||
364 | implicitly called. |
|
|||
365 |
|
||||
366 |
|
||||
367 | The ``code`` field is split into individual blocks each of which is valid for |
|
|||
368 | execution in 'single' mode, and then: |
|
|||
369 |
|
||||
370 | - If there is only a single block: it is executed in 'single' mode. |
|
|||
371 |
|
||||
372 | - If there is more than one block: |
|
|||
373 |
|
||||
374 | * if the last one is a single line long, run all but the last in 'exec' mode |
|
|||
375 | and the very last one in 'single' mode. This makes it easy to type simple |
|
|||
376 | expressions at the end to see computed values. |
|
|||
377 |
|
||||
378 | * if the last one is no more than two lines long, run all but the last in |
|
|||
379 | 'exec' mode and the very last one in 'single' mode. This makes it easy to |
|
|||
380 | type simple expressions at the end to see computed values. - otherwise |
|
|||
381 | (last one is also multiline), run all in 'exec' mode |
|
|||
382 |
|
||||
383 | * otherwise (last one is also multiline), run all in 'exec' mode as a single |
|
|||
384 | unit. |
|
|||
385 |
|
||||
386 | Any error in retrieving the ``user_variables`` or evaluating the |
|
|||
387 | ``user_expressions`` will result in a simple error message in the return fields |
|
|||
388 | of the form:: |
|
|||
389 |
|
||||
390 | [ERROR] ExceptionType: Exception message |
|
|||
391 |
|
||||
392 | The user can simply send the same variable name or expression for evaluation to |
|
|||
393 | see a regular traceback. |
|
|||
394 |
|
||||
395 | Errors in any registered post_execute functions are also reported similarly, |
|
|||
396 | and the failing function is removed from the post_execution set so that it does |
|
|||
397 | not continue triggering failures. |
|
|||
398 |
|
||||
399 | Upon completion of the execution request, the kernel *always* sends a reply, |
|
316 | Upon completion of the execution request, the kernel *always* sends a reply, | |
400 | with a status code indicating what happened and additional data depending on |
|
317 | with a status code indicating what happened and additional data depending on | |
401 | the outcome. See :ref:`below <execution_results>` for the possible return |
|
318 | the outcome. See :ref:`below <execution_results>` for the possible return | |
402 | codes and associated data. |
|
319 | codes and associated data. | |
403 |
|
320 | |||
|
321 | .. seealso:: | |||
|
322 | ||||
|
323 | :ref:`execution_semantics` | |||
404 |
|
324 | |||
405 | .. _execution_counter: |
|
325 | .. _execution_counter: | |
406 |
|
326 | |||
407 |
Execution counter ( |
|
327 | Execution counter (prompt number) | |
408 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
328 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
409 |
|
329 | |||
410 |
The kernel |
|
330 | The kernel should have a single, monotonically increasing counter of all execution | |
411 |
requests that are made with ``store_history=True``. |
|
331 | requests that are made with ``store_history=True``. This counter is used to populate | |
412 | the ``In[n]``, ``Out[n]`` and ``_n`` variables, so clients will likely want to |
|
332 | the ``In[n]`` and ``Out[n]`` prompts. The value of this counter will be returned as the | |
413 | display it in some form to the user, which will typically (but not necessarily) |
|
333 | ``execution_count`` field of all ``execute_reply`` and ``execute_input`` messages. | |
414 | be done in the prompts. The value of this counter will be returned as the |
|
|||
415 | ``execution_count`` field of all ``execute_reply`` and ``pyin`` messages. |
|
|||
416 |
|
334 | |||
417 | .. _execution_results: |
|
335 | .. _execution_results: | |
418 |
|
336 | |||
@@ -444,15 +362,18 When status is 'ok', the following extra fields are present:: | |||||
444 | # which is a string classifying the payload (e.g. 'pager'). |
|
362 | # which is a string classifying the payload (e.g. 'pager'). | |
445 | 'payload' : list(dict), |
|
363 | 'payload' : list(dict), | |
446 |
|
364 | |||
447 |
# Results for the user_ |
|
365 | # Results for the user_expressions. | |
448 | 'user_variables' : dict, |
|
|||
449 | 'user_expressions' : dict, |
|
366 | 'user_expressions' : dict, | |
450 | } |
|
367 | } | |
451 |
|
368 | |||
|
369 | .. versionchanged:: 5.0 | |||
|
370 | ||||
|
371 | ``user_variables`` is removed, use user_expressions instead. | |||
|
372 | ||||
452 | .. admonition:: Execution payloads |
|
373 | .. admonition:: Execution payloads | |
453 |
|
374 | |||
454 | The notion of an 'execution payload' is different from a return value of a |
|
375 | The notion of an 'execution payload' is different from a return value of a | |
455 |
given set of code, which normally is just displayed on the |
|
376 | given set of code, which normally is just displayed on the execute_result stream | |
456 | through the PUB socket. The idea of a payload is to allow special types of |
|
377 | through the PUB socket. The idea of a payload is to allow special types of | |
457 | code, typically magics, to populate a data container in the IPython kernel |
|
378 | code, typically magics, to populate a data container in the IPython kernel | |
458 | that will be shipped back to the caller via this channel. The kernel |
|
379 | that will be shipped back to the caller via this channel. The kernel | |
@@ -489,145 +410,82 When status is 'abort', there are for now no additional data fields. This | |||||
489 | happens when the kernel was interrupted by a signal. |
|
410 | happens when the kernel was interrupted by a signal. | |
490 |
|
411 | |||
491 |
|
412 | |||
492 | Object information |
|
413 | Introspection | |
493 |
------------- |
|
414 | ------------- | |
494 |
|
415 | |||
495 | One of IPython's most used capabilities is the introspection of Python objects |
|
416 | Code can be inspected to show useful information to the user. | |
496 | in the user's namespace, typically invoked via the ``?`` and ``??`` characters |
|
417 | It is up to the Kernel to decide what information should be displayed, and its formatting. | |
497 | (which in reality are shorthands for the ``%pinfo`` magic). This is used often |
|
|||
498 | enough that it warrants an explicit message type, especially because frontends |
|
|||
499 | may want to get object information in response to user keystrokes (like Tab or |
|
|||
500 | F1) besides from the user explicitly typing code like ``x??``. |
|
|||
501 |
|
418 | |||
502 |
Message type: `` |
|
419 | Message type: ``inspect_request``:: | |
503 |
|
420 | |||
504 | content = { |
|
421 | content = { | |
505 | # The (possibly dotted) name of the object to be searched in all |
|
422 | # The code context in which introspection is requested | |
506 | # relevant namespaces |
|
423 | # this may be up to an entire multiline cell. | |
507 |
' |
|
424 | 'code' : str, | |
|
425 | ||||
|
426 | # The cursor position within 'code' (in unicode characters) where inspection is requested | |||
|
427 | 'cursor_pos' : int, | |||
508 |
|
428 | |||
509 |
# The level of detail desired. |
|
429 | # The level of detail desired. In IPython, the default (0) is equivalent to typing | |
510 | # 'x?' at the prompt, 1 is equivalent to 'x??'. |
|
430 | # 'x?' at the prompt, 1 is equivalent to 'x??'. | |
511 | 'detail_level' : int, |
|
431 | # The difference is up to kernels, but in IPython level 1 includes the source code | |
|
432 | # if available. | |||
|
433 | 'detail_level' : 0 or 1, | |||
512 | } |
|
434 | } | |
513 |
|
435 | |||
514 | The returned information will be a dictionary with keys very similar to the |
|
436 | .. versionchanged:: 5.0 | |
515 | field names that IPython prints at the terminal. |
|
|||
516 |
|
437 | |||
517 | Message type: ``object_info_reply``:: |
|
438 | ``object_info_request`` renamed to ``inspect_request``. | |
518 |
|
439 | |||
519 | content = { |
|
440 | .. versionchanged:: 5.0 | |
520 | # The name the object was requested under |
|
|||
521 | 'name' : str, |
|
|||
522 |
|
||||
523 | # Boolean flag indicating whether the named object was found or not. If |
|
|||
524 | # it's false, all other fields will be empty. |
|
|||
525 | 'found' : bool, |
|
|||
526 |
|
||||
527 | # Flags for magics and system aliases |
|
|||
528 | 'ismagic' : bool, |
|
|||
529 | 'isalias' : bool, |
|
|||
530 |
|
||||
531 | # The name of the namespace where the object was found ('builtin', |
|
|||
532 | # 'magics', 'alias', 'interactive', etc.) |
|
|||
533 | 'namespace' : str, |
|
|||
534 |
|
||||
535 | # The type name will be type.__name__ for normal Python objects, but it |
|
|||
536 | # can also be a string like 'Magic function' or 'System alias' |
|
|||
537 | 'type_name' : str, |
|
|||
538 |
|
||||
539 | # The string form of the object, possibly truncated for length if |
|
|||
540 | # detail_level is 0 |
|
|||
541 | 'string_form' : str, |
|
|||
542 |
|
||||
543 | # For objects with a __class__ attribute this will be set |
|
|||
544 | 'base_class' : str, |
|
|||
545 |
|
||||
546 | # For objects with a __len__ attribute this will be set |
|
|||
547 | 'length' : int, |
|
|||
548 |
|
||||
549 | # If the object is a function, class or method whose file we can find, |
|
|||
550 | # we give its full path |
|
|||
551 | 'file' : str, |
|
|||
552 |
|
||||
553 | # For pure Python callable objects, we can reconstruct the object |
|
|||
554 | # definition line which provides its call signature. For convenience this |
|
|||
555 | # is returned as a single 'definition' field, but below the raw parts that |
|
|||
556 | # compose it are also returned as the argspec field. |
|
|||
557 | 'definition' : str, |
|
|||
558 |
|
||||
559 | # The individual parts that together form the definition string. Clients |
|
|||
560 | # with rich display capabilities may use this to provide a richer and more |
|
|||
561 | # precise representation of the definition line (e.g. by highlighting |
|
|||
562 | # arguments based on the user's cursor position). For non-callable |
|
|||
563 | # objects, this field is empty. |
|
|||
564 | 'argspec' : { # The names of all the arguments |
|
|||
565 | args : list, |
|
|||
566 | # The name of the varargs (*args), if any |
|
|||
567 | varargs : str, |
|
|||
568 | # The name of the varkw (**kw), if any |
|
|||
569 | varkw : str, |
|
|||
570 | # The values (as strings) of all default arguments. Note |
|
|||
571 | # that these must be matched *in reverse* with the 'args' |
|
|||
572 | # list above, since the first positional args have no default |
|
|||
573 | # value at all. |
|
|||
574 | defaults : list, |
|
|||
575 | }, |
|
|||
576 |
|
441 | |||
577 | # For instances, provide the constructor signature (the definition of |
|
442 | ``name`` key replaced with ``code`` and ``cursor_pos``, | |
578 | # the __init__ method): |
|
443 | moving the lexing responsibility to the kernel. | |
579 | 'init_definition' : str, |
|
|||
580 |
|
444 | |||
581 | # Docstrings: for any object (function, method, module, package) with a |
|
445 | The reply is a mime-bundle, like a `display_data`_ message, | |
582 | # docstring, we show it. But in addition, we may provide additional |
|
446 | which should be a formatted representation of information about the context. | |
583 | # docstrings. For example, for instances we will show the constructor |
|
447 | In the notebook, this is used to show tooltips over function calls, etc. | |
584 | # and class docstrings as well, if available. |
|
|||
585 | 'docstring' : str, |
|
|||
586 |
|
448 | |||
587 | # For instances, provide the constructor and class docstrings |
|
449 | Message type: ``inspect_reply``:: | |
588 | 'init_docstring' : str, |
|
|||
589 | 'class_docstring' : str, |
|
|||
590 |
|
450 | |||
591 | # If it's a callable object whose call method has a separate docstring and |
|
451 | content = { | |
592 | # definition line: |
|
452 | # 'ok' if the request succeeded or 'error', with error information as in all other replies. | |
593 | 'call_def' : str, |
|
453 | 'status' : 'ok', | |
594 | 'call_docstring' : str, |
|
|||
595 |
|
454 | |||
596 | # If detail_level was 1, we also try to find the source code that |
|
455 | # data can be empty if nothing is found | |
597 | # defines the object, if possible. The string 'None' will indicate |
|
456 | 'data' : dict, | |
598 | # that no source was found. |
|
457 | 'metadata' : dict, | |
599 | 'source' : str, |
|
|||
600 | } |
|
458 | } | |
601 |
|
459 | |||
|
460 | .. versionchanged:: 5.0 | |||
602 |
|
461 | |||
603 | Complete |
|
462 | ``object_info_reply`` renamed to ``inspect_reply``. | |
604 | -------- |
|
|||
605 |
|
463 | |||
606 | Message type: ``complete_request``:: |
|
464 | .. versionchanged:: 5.0 | |
607 |
|
465 | |||
608 | content = { |
|
466 | Reply is changed from structured data to a mime bundle, allowing formatting decisions to be made by the kernel. | |
609 | # The text to be completed, such as 'a.is' |
|
|||
610 | # this may be an empty string if the frontend does not do any lexing, |
|
|||
611 | # in which case the kernel must figure out the completion |
|
|||
612 | # based on 'line' and 'cursor_pos'. |
|
|||
613 | 'text' : str, |
|
|||
614 |
|
467 | |||
615 | # The full line, such as 'print a.is'. This allows completers to |
|
468 | Completion | |
616 | # make decisions that may require information about more than just the |
|
469 | ---------- | |
617 | # current word. |
|
|||
618 | 'line' : str, |
|
|||
619 |
|
470 | |||
620 | # The entire block of text where the line is. This may be useful in the |
|
471 | Message type: ``complete_request``:: | |
621 | # case of multiline completions where more context may be needed. Note: if |
|
|||
622 | # in practice this field proves unnecessary, remove it to lighten the |
|
|||
623 | # messages. |
|
|||
624 |
|
472 | |||
625 | 'block' : str or null/None, |
|
473 | content = { | |
|
474 | # The code context in which completion is requested | |||
|
475 | # this may be up to an entire multiline cell, such as | |||
|
476 | # 'foo = a.isal' | |||
|
477 | 'code' : str, | |||
626 |
|
478 | |||
627 | # The position of the cursor where the user hit 'TAB' on the line. |
|
479 | # The cursor position within 'code' (in unicode characters) where completion is requested | |
628 | 'cursor_pos' : int, |
|
480 | 'cursor_pos' : int, | |
629 | } |
|
481 | } | |
630 |
|
482 | |||
|
483 | .. versionchanged:: 5.0 | |||
|
484 | ||||
|
485 | ``line``, ``block``, and ``text`` keys are removed in favor of a single ``code`` for context. | |||
|
486 | Lexing is up to the kernel. | |||
|
487 | ||||
|
488 | ||||
631 | Message type: ``complete_reply``:: |
|
489 | Message type: ``complete_reply``:: | |
632 |
|
490 | |||
633 | content = { |
|
491 | content = { | |
@@ -635,11 +493,13 Message type: ``complete_reply``:: | |||||
635 | # ['a.isalnum', 'a.isalpha'] for the above example. |
|
493 | # ['a.isalnum', 'a.isalpha'] for the above example. | |
636 | 'matches' : list, |
|
494 | 'matches' : list, | |
637 |
|
495 | |||
638 | # the substring of the matched text |
|
496 | # The range of text that should be replaced by the above matches when a completion is accepted. | |
639 | # this is typically the common prefix of the matches, |
|
497 | # typically cursor_end is the same as cursor_pos in the request. | |
640 | # and the text that is already in the block that would be replaced by the full completion. |
|
498 | 'cursor_start' : int, | |
641 | # This would be 'a.is' in the above example. |
|
499 | 'cursor_end' : int, | |
642 | 'matched_text' : str, |
|
500 | ||
|
501 | # Information that frontend plugins might use for extra display information about completions. | |||
|
502 | 'metadata' : dict, | |||
643 |
|
503 | |||
644 | # status should be 'ok' unless an exception was raised during the request, |
|
504 | # status should be 'ok' unless an exception was raised during the request, | |
645 | # in which case it should be 'error', along with the usual error message content |
|
505 | # in which case it should be 'error', along with the usual error message content | |
@@ -647,6 +507,11 Message type: ``complete_reply``:: | |||||
647 | 'status' : 'ok' |
|
507 | 'status' : 'ok' | |
648 | } |
|
508 | } | |
649 |
|
509 | |||
|
510 | .. versionchanged:: 5.0 | |||
|
511 | ||||
|
512 | - ``matched_text`` is removed in favor of ``cursor_start`` and ``cursor_end``. | |||
|
513 | - ``metadata`` is added for extended information. | |||
|
514 | ||||
650 |
|
515 | |||
651 | History |
|
516 | History | |
652 | ------- |
|
517 | ------- | |
@@ -743,30 +608,48 Message type: ``kernel_info_request``:: | |||||
743 | Message type: ``kernel_info_reply``:: |
|
608 | Message type: ``kernel_info_reply``:: | |
744 |
|
609 | |||
745 | content = { |
|
610 | content = { | |
746 |
# Version of messaging protocol |
|
611 | # Version of messaging protocol. | |
747 | # The first integer indicates major version. It is incremented when |
|
612 | # The first integer indicates major version. It is incremented when | |
748 | # there is any backward incompatible change. |
|
613 | # there is any backward incompatible change. | |
749 | # The second integer indicates minor version. It is incremented when |
|
614 | # The second integer indicates minor version. It is incremented when | |
750 | # there is any backward compatible change. |
|
615 | # there is any backward compatible change. | |
751 |
'protocol_version': |
|
616 | 'protocol_version': 'X.Y.Z', | |
752 |
|
617 | |||
753 | # IPython version number (optional). |
|
618 | # The kernel implementation name | |
754 | # Non-python kernel backend may not have this version number. |
|
619 | # (e.g. 'ipython' for the IPython kernel) | |
755 | # The last component is an extra field, which may be 'dev' or |
|
620 | 'implementation': str, | |
756 | # 'rc1' in development version. It is an empty string for |
|
|||
757 | # released version. |
|
|||
758 | 'ipython_version': [int, int, int, str], |
|
|||
759 |
|
621 | |||
760 |
# |
|
622 | # Implementation version number. | |
761 |
# |
|
623 | # The version number of the kernel's implementation | |
762 | # included in IPython. |
|
624 | # (e.g. IPython.__version__ for the IPython kernel) | |
763 |
' |
|
625 | 'implementation_version': 'X.Y.Z', | |
764 |
|
626 | |||
765 |
# Programming language in which kernel is implemented |
|
627 | # Programming language in which kernel is implemented. | |
766 | # Kernel included in IPython returns 'python'. |
|
628 | # Kernel included in IPython returns 'python'. | |
767 | 'language': str, |
|
629 | 'language': str, | |
|
630 | ||||
|
631 | # Language version number. | |||
|
632 | # It is Python version number (e.g., '2.7.3') for the kernel | |||
|
633 | # included in IPython. | |||
|
634 | 'language_version': 'X.Y.Z', | |||
|
635 | ||||
|
636 | # A banner of information about the kernel, | |||
|
637 | # which may be desplayed in console environments. | |||
|
638 | 'banner' : str, | |||
768 | } |
|
639 | } | |
769 |
|
640 | |||
|
641 | .. versionchanged:: 5.0 | |||
|
642 | ||||
|
643 | Versions changed from lists of integers to strings. | |||
|
644 | ||||
|
645 | .. versionchanged:: 5.0 | |||
|
646 | ||||
|
647 | ``ipython_version`` is removed. | |||
|
648 | ||||
|
649 | .. versionchanged:: 5.0 | |||
|
650 | ||||
|
651 | ``implementation``, ``implementation_version``, and ``banner`` keys are added. | |||
|
652 | ||||
770 |
|
653 | |||
771 | Kernel shutdown |
|
654 | Kernel shutdown | |
772 | --------------- |
|
655 | --------------- | |
@@ -834,6 +717,14 frontend to decide which to use and how. A single message should contain all | |||||
834 | possible representations of the same information. Each representation should |
|
717 | possible representations of the same information. Each representation should | |
835 | be a JSON'able data structure, and should be a valid MIME type. |
|
718 | be a JSON'able data structure, and should be a valid MIME type. | |
836 |
|
719 | |||
|
720 | Some questions remain about this design: | |||
|
721 | ||||
|
722 | * Do we use this message type for execute_result/displayhook? Probably not, because | |||
|
723 | the displayhook also has to handle the Out prompt display. On the other hand | |||
|
724 | we could put that information into the metadata section. | |||
|
725 | ||||
|
726 | .. _display_data: | |||
|
727 | ||||
837 | Message type: ``display_data``:: |
|
728 | Message type: ``display_data``:: | |
838 |
|
729 | |||
839 | content = { |
|
730 | content = { | |
@@ -861,7 +752,7 with a reasonably unique name to avoid conflicts. | |||||
861 | The only metadata keys currently defined in IPython are the width and height |
|
752 | The only metadata keys currently defined in IPython are the width and height | |
862 | of images:: |
|
753 | of images:: | |
863 |
|
754 | |||
864 |
|
|
755 | metadata = { | |
865 | 'image/png' : { |
|
756 | 'image/png' : { | |
866 | 'width': 640, |
|
757 | 'width': 640, | |
867 | 'height': 480 |
|
758 | 'height': 480 | |
@@ -869,6 +760,12 of images:: | |||||
869 | } |
|
760 | } | |
870 |
|
761 | |||
871 |
|
762 | |||
|
763 | .. versionchanged:: 5.0 | |||
|
764 | ||||
|
765 | `application/json` data should be unpacked JSON data, | |||
|
766 | not double-serialized as a JSON string. | |||
|
767 | ||||
|
768 | ||||
872 | Raw Data Publication |
|
769 | Raw Data Publication | |
873 | -------------------- |
|
770 | -------------------- | |
874 |
|
771 | |||
@@ -888,11 +785,11 Message type: ``data_pub``:: | |||||
888 |
|
785 | |||
889 | content = { |
|
786 | content = { | |
890 | # the keys of the data dict, after it has been unserialized |
|
787 | # the keys of the data dict, after it has been unserialized | |
891 |
|
|
788 | 'keys' : ['a', 'b'] | |
892 | } |
|
789 | } | |
893 | # the namespace dict will be serialized in the message buffers, |
|
790 | # the namespace dict will be serialized in the message buffers, | |
894 | # which will have a length of at least one |
|
791 | # which will have a length of at least one | |
895 | buffers = ['pdict', ...] |
|
792 | buffers = [b'pdict', ...] | |
896 |
|
793 | |||
897 |
|
794 | |||
898 | The interpretation of a sequence of data_pub messages for a given parent request should be |
|
795 | The interpretation of a sequence of data_pub messages for a given parent request should be | |
@@ -906,15 +803,15 to update a single namespace with subsequent results. | |||||
906 | of which the Client can then publish *representations* via ``display_data`` |
|
803 | of which the Client can then publish *representations* via ``display_data`` | |
907 | to various frontends. |
|
804 | to various frontends. | |
908 |
|
805 | |||
909 |
|
|
806 | Code inputs | |
910 |
----------- |
|
807 | ----------- | |
911 |
|
808 | |||
912 | To let all frontends know what code is being executed at any given time, these |
|
809 | To let all frontends know what code is being executed at any given time, these | |
913 | messages contain a re-broadcast of the ``code`` portion of an |
|
810 | messages contain a re-broadcast of the ``code`` portion of an | |
914 | :ref:`execute_request <execute>`, along with the :ref:`execution_count |
|
811 | :ref:`execute_request <execute>`, along with the :ref:`execution_count | |
915 | <execution_counter>`. |
|
812 | <execution_counter>`. | |
916 |
|
813 | |||
917 |
Message type: `` |
|
814 | Message type: ``execute_input``:: | |
918 |
|
815 | |||
919 | content = { |
|
816 | content = { | |
920 | 'code' : str, # Source code to be executed, one or more lines |
|
817 | 'code' : str, # Source code to be executed, one or more lines | |
@@ -925,32 +822,25 Message type: ``pyin``:: | |||||
925 | 'execution_count' : int |
|
822 | 'execution_count' : int | |
926 | } |
|
823 | } | |
927 |
|
824 | |||
928 | Python outputs |
|
825 | .. versionchanged:: 5.0 | |
929 | -------------- |
|
826 | ||
|
827 | ``pyin`` is renamed to ``execute_input``. | |||
930 |
|
828 | |||
931 | When Python produces output from code that has been compiled in with the |
|
829 | ||
932 | 'single' flag to :func:`compile`, any expression that produces a value (such as |
|
830 | Execution results | |
933 | ``1+1``) is passed to ``sys.displayhook``, which is a callable that can do with |
|
831 | ----------------- | |
934 | this value whatever it wants. The default behavior of ``sys.displayhook`` in |
|
832 | ||
935 | the Python interactive prompt is to print to ``sys.stdout`` the :func:`repr` of |
|
833 | Results of an execution are published as an ``execute_result``. | |
936 | the value as long as it is not ``None`` (which isn't printed at all). In our |
|
834 | These are identical to `display_data`_ messages, with the addition of an ``execution_count`` key. | |
937 | case, the kernel instantiates as ``sys.displayhook`` an object which has |
|
835 | ||
938 | similar behavior, but which instead of printing to stdout, broadcasts these |
|
836 | Results can have multiple simultaneous formats depending on its | |
939 | values as ``pyout`` messages for clients to display appropriately. |
|
837 | configuration. A plain text representation should always be provided | |
940 |
|
838 | in the ``text/plain`` mime-type. Frontends are free to display any or all of these | ||
941 | IPython's displayhook can handle multiple simultaneous formats depending on its |
|
839 | according to its capabilities. | |
942 | configuration. The default pretty-printed repr text is always given with the |
|
840 | Frontends should ignore mime-types they do not understand. The data itself is | |
943 | ``data`` entry in this message. Any other formats are provided in the |
|
|||
944 | ``extra_formats`` list. Frontends are free to display any or all of these |
|
|||
945 | according to its capabilities. ``extra_formats`` list contains 3-tuples of an ID |
|
|||
946 | string, a type string, and the data. The ID is unique to the formatter |
|
|||
947 | implementation that created the data. Frontends will typically ignore the ID |
|
|||
948 | unless if it has requested a particular formatter. The type string tells the |
|
|||
949 | frontend how to interpret the data. It is often, but not always a MIME type. |
|
|||
950 | Frontends should ignore types that it does not understand. The data itself is |
|
|||
951 | any JSON object and depends on the format. It is often, but not always a string. |
|
841 | any JSON object and depends on the format. It is often, but not always a string. | |
952 |
|
842 | |||
953 |
Message type: `` |
|
843 | Message type: ``execute_result``:: | |
954 |
|
844 | |||
955 | content = { |
|
845 | content = { | |
956 |
|
846 | |||
@@ -966,18 +856,22 Message type: ``pyout``:: | |||||
966 | 'metadata' : dict, |
|
856 | 'metadata' : dict, | |
967 | } |
|
857 | } | |
968 |
|
858 | |||
969 |
|
|
859 | Execution errors | |
970 | ------------- |
|
860 | ---------------- | |
971 |
|
861 | |||
972 | When an error occurs during code execution |
|
862 | When an error occurs during code execution | |
973 |
|
863 | |||
974 |
Message type: `` |
|
864 | Message type: ``error``:: | |
975 |
|
865 | |||
976 | content = { |
|
866 | content = { | |
977 | # Similar content to the execute_reply messages for the 'error' case, |
|
867 | # Similar content to the execute_reply messages for the 'error' case, | |
978 | # except the 'status' field is omitted. |
|
868 | # except the 'status' field is omitted. | |
979 | } |
|
869 | } | |
980 |
|
870 | |||
|
871 | .. versionchanged:: 5.0 | |||
|
872 | ||||
|
873 | ``pyerr`` renamed to ``error`` | |||
|
874 | ||||
981 | Kernel status |
|
875 | Kernel status | |
982 | ------------- |
|
876 | ------------- | |
983 |
|
877 | |||
@@ -1009,8 +903,8 Message type: ``clear_output``:: | |||||
1009 |
|
903 | |||
1010 | .. versionchanged:: 4.1 |
|
904 | .. versionchanged:: 4.1 | |
1011 |
|
905 | |||
1012 |
|
|
906 | ``stdout``, ``stderr``, and ``display`` boolean keys for selective clearing are removed, | |
1013 |
and |
|
907 | and ``wait`` is added. | |
1014 | The selective clearing keys are ignored in v4 and the default behavior remains the same, |
|
908 | The selective clearing keys are ignored in v4 and the default behavior remains the same, | |
1015 | so v4 clear_output messages will be safely handled by a v4.1 frontend. |
|
909 | so v4 clear_output messages will be safely handled by a v4.1 frontend. | |
1016 |
|
910 | |||
@@ -1028,12 +922,25 the ``raw_input(prompt)`` call. | |||||
1028 |
|
922 | |||
1029 | Message type: ``input_request``:: |
|
923 | Message type: ``input_request``:: | |
1030 |
|
924 | |||
1031 |
content = { |
|
925 | content = { | |
|
926 | # the text to show at the prompt | |||
|
927 | 'prompt' : str, | |||
|
928 | # Is the request for a password? | |||
|
929 | # If so, the frontend shouldn't echo input. | |||
|
930 | 'password' : bool | |||
|
931 | } | |||
1032 |
|
|
932 | ||
1033 | Message type: ``input_reply``:: |
|
933 | Message type: ``input_reply``:: | |
1034 |
|
934 | |||
1035 | content = { 'value' : str } |
|
935 | content = { 'value' : str } | |
1036 |
|
936 | |||
|
937 | ||||
|
938 | When ``password`` is True, the frontend should not echo the input as it is entered. | |||
|
939 | ||||
|
940 | .. versionchanged:: 5.0 | |||
|
941 | ||||
|
942 | ``password`` key added. | |||
|
943 | ||||
1037 | .. note:: |
|
944 | .. note:: | |
1038 |
|
945 | |||
1039 | The stdin socket of the client is required to have the same zmq IDENTITY |
|
946 | The stdin socket of the client is required to have the same zmq IDENTITY | |
@@ -1057,30 +964,9 Message type: ``input_reply``:: | |||||
1057 | Heartbeat for kernels |
|
964 | Heartbeat for kernels | |
1058 | ===================== |
|
965 | ===================== | |
1059 |
|
966 | |||
1060 | Initially we had considered using messages like those above over ZMQ for a |
|
967 | Clients send ping messages on a REQ socket, which are echoed right back | |
1061 | kernel 'heartbeat' (a way to detect quickly and reliably whether a kernel is |
|
968 | from the Kernel's REP socket. These are simple bytestrings, not full JSON messages described above. | |
1062 | alive at all, even if it may be busy executing user code). But this has the |
|
|||
1063 | problem that if the kernel is locked inside extension code, it wouldn't execute |
|
|||
1064 | the python heartbeat code. But it turns out that we can implement a basic |
|
|||
1065 | heartbeat with pure ZMQ, without using any Python messaging at all. |
|
|||
1066 |
|
||||
1067 | The monitor sends out a single zmq message (right now, it is a str of the |
|
|||
1068 | monitor's lifetime in seconds), and gets the same message right back, prefixed |
|
|||
1069 | with the zmq identity of the DEALER socket in the heartbeat process. This can be |
|
|||
1070 | a uuid, or even a full message, but there doesn't seem to be a need for packing |
|
|||
1071 | up a message when the sender and receiver are the exact same Python object. |
|
|||
1072 |
|
||||
1073 | The model is this:: |
|
|||
1074 |
|
||||
1075 | monitor.send(str(self.lifetime)) # '1.2345678910' |
|
|||
1076 |
|
969 | |||
1077 | and the monitor receives some number of messages of the form:: |
|
|||
1078 |
|
||||
1079 | ['uuid-abcd-dead-beef', '1.2345678910'] |
|
|||
1080 |
|
||||
1081 | where the first part is the zmq.IDENTITY of the heart's DEALER on the engine, and |
|
|||
1082 | the rest is the message sent by the monitor. No Python code ever has any |
|
|||
1083 | access to the message between the monitor's send, and the monitor's recv. |
|
|||
1084 |
|
970 | |||
1085 | Custom Messages |
|
971 | Custom Messages | |
1086 | =============== |
|
972 | =============== | |
@@ -1157,14 +1043,10 just like an execute request. | |||||
1157 |
|
1043 | |||
1158 |
|
1044 | |||
1159 | ToDo |
|
1045 | To Do | |
1160 | ==== |
|
1046 | ===== | |
1161 |
|
1047 | |||
1162 | Missing things include: |
|
1048 | Missing things include: | |
1163 |
|
1049 | |||
1164 | * Important: finish thinking through the payload concept and API. |
|
1050 | * Important: finish thinking through the payload concept and API. | |
1165 |
|
1051 | |||
1166 | * Important: ensure that we have a good solution for magics like %edit. It's |
|
|||
1167 | likely that with the payload concept we can build a full solution, but not |
|
|||
1168 | 100% clear yet. |
|
|||
1169 |
|
||||
1170 | .. include:: ../links.txt |
|
1052 | .. include:: ../links.txt |
General Comments 0
You need to be logged in to leave comments.
Login now