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