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