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