##// END OF EJS Templates
move _encode_binary to jsonutil.encode_images...
MinRK -
Show More
@@ -1,166 +1,185 b''
1 """Utilities to manipulate JSON objects.
1 """Utilities to manipulate JSON objects.
2 """
2 """
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (C) 2010-2011 The IPython Development Team
4 # Copyright (C) 2010-2011 The IPython Development Team
5 #
5 #
6 # Distributed under the terms of the BSD License. The full license is in
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.
7 # the file COPYING.txt, distributed as part of this software.
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Imports
11 # Imports
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # stdlib
13 # stdlib
14 import re
14 import re
15 import sys
15 import sys
16 import types
16 import types
17 from base64 import encodestring
17 from datetime import datetime
18 from datetime import datetime
18
19
19 from IPython.utils import py3compat
20 from IPython.utils import py3compat
20 from IPython.utils.encoding import DEFAULT_ENCODING
21 from IPython.utils.encoding import DEFAULT_ENCODING
21 from IPython.utils import text
22 from IPython.utils import text
22 next_attr_name = '__next__' if py3compat.PY3 else 'next'
23 next_attr_name = '__next__' if py3compat.PY3 else 'next'
23
24
24 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
25 # Globals and constants
26 # Globals and constants
26 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
27
28
28 # timestamp formats
29 # timestamp formats
29 ISO8601="%Y-%m-%dT%H:%M:%S.%f"
30 ISO8601="%Y-%m-%dT%H:%M:%S.%f"
30 ISO8601_PAT=re.compile(r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+$")
31 ISO8601_PAT=re.compile(r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+$")
31
32
32 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
33 # Classes and functions
34 # Classes and functions
34 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
35
36
36 def rekey(dikt):
37 def rekey(dikt):
37 """Rekey a dict that has been forced to use str keys where there should be
38 """Rekey a dict that has been forced to use str keys where there should be
38 ints by json."""
39 ints by json."""
39 for k in dikt.iterkeys():
40 for k in dikt.iterkeys():
40 if isinstance(k, basestring):
41 if isinstance(k, basestring):
41 ik=fk=None
42 ik=fk=None
42 try:
43 try:
43 ik = int(k)
44 ik = int(k)
44 except ValueError:
45 except ValueError:
45 try:
46 try:
46 fk = float(k)
47 fk = float(k)
47 except ValueError:
48 except ValueError:
48 continue
49 continue
49 if ik is not None:
50 if ik is not None:
50 nk = ik
51 nk = ik
51 else:
52 else:
52 nk = fk
53 nk = fk
53 if nk in dikt:
54 if nk in dikt:
54 raise KeyError("already have key %r"%nk)
55 raise KeyError("already have key %r"%nk)
55 dikt[nk] = dikt.pop(k)
56 dikt[nk] = dikt.pop(k)
56 return dikt
57 return dikt
57
58
58
59
59 def extract_dates(obj):
60 def extract_dates(obj):
60 """extract ISO8601 dates from unpacked JSON"""
61 """extract ISO8601 dates from unpacked JSON"""
61 if isinstance(obj, dict):
62 if isinstance(obj, dict):
62 obj = dict(obj) # don't clobber
63 obj = dict(obj) # don't clobber
63 for k,v in obj.iteritems():
64 for k,v in obj.iteritems():
64 obj[k] = extract_dates(v)
65 obj[k] = extract_dates(v)
65 elif isinstance(obj, (list, tuple)):
66 elif isinstance(obj, (list, tuple)):
66 obj = [ extract_dates(o) for o in obj ]
67 obj = [ extract_dates(o) for o in obj ]
67 elif isinstance(obj, basestring):
68 elif isinstance(obj, basestring):
68 if ISO8601_PAT.match(obj):
69 if ISO8601_PAT.match(obj):
69 obj = datetime.strptime(obj, ISO8601)
70 obj = datetime.strptime(obj, ISO8601)
70 return obj
71 return obj
71
72
72 def squash_dates(obj):
73 def squash_dates(obj):
73 """squash datetime objects into ISO8601 strings"""
74 """squash datetime objects into ISO8601 strings"""
74 if isinstance(obj, dict):
75 if isinstance(obj, dict):
75 obj = dict(obj) # don't clobber
76 obj = dict(obj) # don't clobber
76 for k,v in obj.iteritems():
77 for k,v in obj.iteritems():
77 obj[k] = squash_dates(v)
78 obj[k] = squash_dates(v)
78 elif isinstance(obj, (list, tuple)):
79 elif isinstance(obj, (list, tuple)):
79 obj = [ squash_dates(o) for o in obj ]
80 obj = [ squash_dates(o) for o in obj ]
80 elif isinstance(obj, datetime):
81 elif isinstance(obj, datetime):
81 obj = obj.strftime(ISO8601)
82 obj = obj.strftime(ISO8601)
82 return obj
83 return obj
83
84
84 def date_default(obj):
85 def date_default(obj):
85 """default function for packing datetime objects in JSON."""
86 """default function for packing datetime objects in JSON."""
86 if isinstance(obj, datetime):
87 if isinstance(obj, datetime):
87 return obj.strftime(ISO8601)
88 return obj.strftime(ISO8601)
88 else:
89 else:
89 raise TypeError("%r is not JSON serializable"%obj)
90 raise TypeError("%r is not JSON serializable"%obj)
90
91
91
92
93 # constants for identifying png/jpeg data
94 PNG = b'\x89PNG\r\n\x1a\n'
95 JPEG = b'\xff\xd8'
96
97 def encode_images(format_dict):
98 """b64-encodes images in a displaypub format dict
99
100 Perhaps this should be handled in json_clean itself?
101 """
102 encoded = format_dict.copy()
103 pngdata = format_dict.get('image/png')
104 if isinstance(pngdata, bytes) and pngdata[:8] == PNG:
105 encoded['image/png'] = encodestring(pngdata).decode('ascii')
106 jpegdata = format_dict.get('image/jpeg')
107 if isinstance(jpegdata, bytes) and jpegdata[:2] == JPEG:
108 encoded['image/jpeg'] = encodestring(jpegdata).decode('ascii')
109 return encoded
110
92
111
93 def json_clean(obj):
112 def json_clean(obj):
94 """Clean an object to ensure it's safe to encode in JSON.
113 """Clean an object to ensure it's safe to encode in JSON.
95
114
96 Atomic, immutable objects are returned unmodified. Sets and tuples are
115 Atomic, immutable objects are returned unmodified. Sets and tuples are
97 converted to lists, lists are copied and dicts are also copied.
116 converted to lists, lists are copied and dicts are also copied.
98
117
99 Note: dicts whose keys could cause collisions upon encoding (such as a dict
118 Note: dicts whose keys could cause collisions upon encoding (such as a dict
100 with both the number 1 and the string '1' as keys) will cause a ValueError
119 with both the number 1 and the string '1' as keys) will cause a ValueError
101 to be raised.
120 to be raised.
102
121
103 Parameters
122 Parameters
104 ----------
123 ----------
105 obj : any python object
124 obj : any python object
106
125
107 Returns
126 Returns
108 -------
127 -------
109 out : object
128 out : object
110
129
111 A version of the input which will not cause an encoding error when
130 A version of the input which will not cause an encoding error when
112 encoded as JSON. Note that this function does not *encode* its inputs,
131 encoded as JSON. Note that this function does not *encode* its inputs,
113 it simply sanitizes it so that there will be no encoding errors later.
132 it simply sanitizes it so that there will be no encoding errors later.
114
133
115 Examples
134 Examples
116 --------
135 --------
117 >>> json_clean(4)
136 >>> json_clean(4)
118 4
137 4
119 >>> json_clean(range(10))
138 >>> json_clean(range(10))
120 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
139 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
121 >>> sorted(json_clean(dict(x=1, y=2)).items())
140 >>> sorted(json_clean(dict(x=1, y=2)).items())
122 [('x', 1), ('y', 2)]
141 [('x', 1), ('y', 2)]
123 >>> sorted(json_clean(dict(x=1, y=2, z=[1,2,3])).items())
142 >>> sorted(json_clean(dict(x=1, y=2, z=[1,2,3])).items())
124 [('x', 1), ('y', 2), ('z', [1, 2, 3])]
143 [('x', 1), ('y', 2), ('z', [1, 2, 3])]
125 >>> json_clean(True)
144 >>> json_clean(True)
126 True
145 True
127 """
146 """
128 # types that are 'atomic' and ok in json as-is. bool doesn't need to be
147 # types that are 'atomic' and ok in json as-is. bool doesn't need to be
129 # listed explicitly because bools pass as int instances
148 # listed explicitly because bools pass as int instances
130 atomic_ok = (unicode, int, float, types.NoneType)
149 atomic_ok = (unicode, int, float, types.NoneType)
131
150
132 # containers that we need to convert into lists
151 # containers that we need to convert into lists
133 container_to_list = (tuple, set, types.GeneratorType)
152 container_to_list = (tuple, set, types.GeneratorType)
134
153
135 if isinstance(obj, atomic_ok):
154 if isinstance(obj, atomic_ok):
136 return obj
155 return obj
137
156
138 if isinstance(obj, bytes):
157 if isinstance(obj, bytes):
139 return obj.decode(DEFAULT_ENCODING, 'replace')
158 return obj.decode(DEFAULT_ENCODING, 'replace')
140
159
141 if isinstance(obj, container_to_list) or (
160 if isinstance(obj, container_to_list) or (
142 hasattr(obj, '__iter__') and hasattr(obj, next_attr_name)):
161 hasattr(obj, '__iter__') and hasattr(obj, next_attr_name)):
143 obj = list(obj)
162 obj = list(obj)
144
163
145 if isinstance(obj, list):
164 if isinstance(obj, list):
146 return [json_clean(x) for x in obj]
165 return [json_clean(x) for x in obj]
147
166
148 if isinstance(obj, dict):
167 if isinstance(obj, dict):
149 # First, validate that the dict won't lose data in conversion due to
168 # First, validate that the dict won't lose data in conversion due to
150 # key collisions after stringification. This can happen with keys like
169 # key collisions after stringification. This can happen with keys like
151 # True and 'true' or 1 and '1', which collide in JSON.
170 # True and 'true' or 1 and '1', which collide in JSON.
152 nkeys = len(obj)
171 nkeys = len(obj)
153 nkeys_collapsed = len(set(map(str, obj)))
172 nkeys_collapsed = len(set(map(str, obj)))
154 if nkeys != nkeys_collapsed:
173 if nkeys != nkeys_collapsed:
155 raise ValueError('dict can not be safely converted to JSON: '
174 raise ValueError('dict can not be safely converted to JSON: '
156 'key collision would lead to dropped values')
175 'key collision would lead to dropped values')
157 # If all OK, proceed by making the new dict that will be json-safe
176 # If all OK, proceed by making the new dict that will be json-safe
158 out = {}
177 out = {}
159 for k,v in obj.iteritems():
178 for k,v in obj.iteritems():
160 out[str(k)] = json_clean(v)
179 out[str(k)] = json_clean(v)
161 return out
180 return out
162
181
163 # If we get here, we don't know how to handle the object, so we just get
182 # If we get here, we don't know how to handle the object, so we just get
164 # its repr and return that. This will catch lambdas, open sockets, class
183 # its repr and return that. This will catch lambdas, open sockets, class
165 # objects, and any other complicated contraption that json can't encode
184 # objects, and any other complicated contraption that json can't encode
166 return repr(obj)
185 return repr(obj)
@@ -1,72 +1,104 b''
1 """Test suite for our JSON utilities.
1 """Test suite for our JSON utilities.
2 """
2 """
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (C) 2010-2011 The IPython Development Team
4 # Copyright (C) 2010-2011 The IPython Development Team
5 #
5 #
6 # Distributed under the terms of the BSD License. The full license is in
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.
7 # the file COPYING.txt, distributed as part of this software.
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Imports
11 # Imports
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # stdlib
13 # stdlib
14 import json
14 import json
15 from base64 import decodestring
15
16
16 # third party
17 # third party
17 import nose.tools as nt
18 import nose.tools as nt
18
19
19 # our own
20 # our own
20 from ..jsonutil import json_clean
21 from IPython.testing import decorators as dec
22 from ..jsonutil import json_clean, encode_images
23 from ..py3compat import unicode_to_str, str_to_bytes
21
24
22 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
23 # Test functions
26 # Test functions
24 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
25
28
26 def test():
29 def test():
27 # list of input/expected output. Use None for the expected output if it
30 # list of input/expected output. Use None for the expected output if it
28 # can be the same as the input.
31 # can be the same as the input.
29 pairs = [(1, None), # start with scalars
32 pairs = [(1, None), # start with scalars
30 (1.0, None),
33 (1.0, None),
31 ('a', None),
34 ('a', None),
32 (True, None),
35 (True, None),
33 (False, None),
36 (False, None),
34 (None, None),
37 (None, None),
35 # complex numbers for now just go to strings, as otherwise they
38 # complex numbers for now just go to strings, as otherwise they
36 # are unserializable
39 # are unserializable
37 (1j, '1j'),
40 (1j, '1j'),
38 # Containers
41 # Containers
39 ([1, 2], None),
42 ([1, 2], None),
40 ((1, 2), [1, 2]),
43 ((1, 2), [1, 2]),
41 (set([1, 2]), [1, 2]),
44 (set([1, 2]), [1, 2]),
42 (dict(x=1), None),
45 (dict(x=1), None),
43 ({'x': 1, 'y':[1,2,3], '1':'int'}, None),
46 ({'x': 1, 'y':[1,2,3], '1':'int'}, None),
44 # More exotic objects
47 # More exotic objects
45 ((x for x in range(3)), [0, 1, 2]),
48 ((x for x in range(3)), [0, 1, 2]),
46 (iter([1, 2]), [1, 2]),
49 (iter([1, 2]), [1, 2]),
47 ]
50 ]
48
51
49 for val, jval in pairs:
52 for val, jval in pairs:
50 if jval is None:
53 if jval is None:
51 jval = val
54 jval = val
52 out = json_clean(val)
55 out = json_clean(val)
53 # validate our cleanup
56 # validate our cleanup
54 nt.assert_equal(out, jval)
57 nt.assert_equal(out, jval)
55 # and ensure that what we return, indeed encodes cleanly
58 # and ensure that what we return, indeed encodes cleanly
56 json.loads(json.dumps(out))
59 json.loads(json.dumps(out))
57
60
58
61
62
63 @dec.parametric
64 def test_encode_images():
65 # invalid data, but the header and footer are from real files
66 pngdata = b'\x89PNG\r\n\x1a\nblahblahnotactuallyvalidIEND\xaeB`\x82'
67 jpegdata = b'\xff\xd8\xff\xe0\x00\x10JFIFblahblahjpeg(\xa0\x0f\xff\xd9'
68
69 fmt = {
70 'image/png' : pngdata,
71 'image/jpeg' : jpegdata,
72 }
73 encoded = encode_images(fmt)
74 for key, value in fmt.iteritems():
75 # encoded has unicode, want bytes
76 decoded = decodestring(encoded[key].encode('ascii'))
77 yield nt.assert_equal(decoded, value)
78 encoded2 = encode_images(encoded)
79 yield nt.assert_equal(encoded, encoded2)
80
81 b64_str = {}
82 for key, encoded in encoded.iteritems():
83 b64_str[key] = unicode_to_str(encoded)
84 encoded3 = encode_images(b64_str)
85 yield nt.assert_equal(encoded3, b64_str)
86 for key, value in fmt.iteritems():
87 # encoded3 has str, want bytes
88 decoded = decodestring(str_to_bytes(encoded3[key]))
89 yield nt.assert_equal(decoded, value)
90
59 def test_lambda():
91 def test_lambda():
60 jc = json_clean(lambda : 1)
92 jc = json_clean(lambda : 1)
61 assert isinstance(jc, str)
93 assert isinstance(jc, str)
62 assert '<lambda>' in jc
94 assert '<lambda>' in jc
63 json.dumps(jc)
95 json.dumps(jc)
64
96
65
97
66 def test_exception():
98 def test_exception():
67 bad_dicts = [{1:'number', '1':'string'},
99 bad_dicts = [{1:'number', '1':'string'},
68 {True:'bool', 'True':'string'},
100 {True:'bool', 'True':'string'},
69 ]
101 ]
70 for d in bad_dicts:
102 for d in bad_dicts:
71 nt.assert_raises(ValueError, json_clean, d)
103 nt.assert_raises(ValueError, json_clean, d)
72
104
@@ -1,75 +1,63 b''
1 import __builtin__
1 import __builtin__
2 import sys
2 import sys
3 from base64 import encodestring
4
3
5 from IPython.core.displayhook import DisplayHook
4 from IPython.core.displayhook import DisplayHook
5 from IPython.utils.jsonutil import encode_images
6 from IPython.utils.traitlets import Instance, Dict
6 from IPython.utils.traitlets import Instance, Dict
7 from session import extract_header, Session
7 from session import extract_header, Session
8
8
9 class ZMQDisplayHook(object):
9 class ZMQDisplayHook(object):
10 """A simple displayhook that publishes the object's repr over a ZeroMQ
10 """A simple displayhook that publishes the object's repr over a ZeroMQ
11 socket."""
11 socket."""
12 topic=None
12 topic=None
13
13
14 def __init__(self, session, pub_socket):
14 def __init__(self, session, pub_socket):
15 self.session = session
15 self.session = session
16 self.pub_socket = pub_socket
16 self.pub_socket = pub_socket
17 self.parent_header = {}
17 self.parent_header = {}
18
18
19 def __call__(self, obj):
19 def __call__(self, obj):
20 if obj is None:
20 if obj is None:
21 return
21 return
22
22
23 __builtin__._ = obj
23 __builtin__._ = obj
24 sys.stdout.flush()
24 sys.stdout.flush()
25 sys.stderr.flush()
25 sys.stderr.flush()
26 msg = self.session.send(self.pub_socket, u'pyout', {u'data':repr(obj)},
26 msg = self.session.send(self.pub_socket, u'pyout', {u'data':repr(obj)},
27 parent=self.parent_header, ident=self.topic)
27 parent=self.parent_header, ident=self.topic)
28
28
29 def set_parent(self, parent):
29 def set_parent(self, parent):
30 self.parent_header = extract_header(parent)
30 self.parent_header = extract_header(parent)
31
31
32
32
33 def _encode_binary(format_dict):
34 encoded = format_dict.copy()
35 pngdata = format_dict.get('image/png')
36 if isinstance(pngdata, bytes):
37 encoded['image/png'] = encodestring(pngdata).decode('ascii')
38 jpegdata = format_dict.get('image/jpeg')
39 if isinstance(jpegdata, bytes):
40 encoded['image/jpeg'] = encodestring(jpegdata).decode('ascii')
41
42 return encoded
43
44
45 class ZMQShellDisplayHook(DisplayHook):
33 class ZMQShellDisplayHook(DisplayHook):
46 """A displayhook subclass that publishes data using ZeroMQ. This is intended
34 """A displayhook subclass that publishes data using ZeroMQ. This is intended
47 to work with an InteractiveShell instance. It sends a dict of different
35 to work with an InteractiveShell instance. It sends a dict of different
48 representations of the object."""
36 representations of the object."""
49 topic=None
37 topic=None
50
38
51 session = Instance(Session)
39 session = Instance(Session)
52 pub_socket = Instance('zmq.Socket')
40 pub_socket = Instance('zmq.Socket')
53 parent_header = Dict({})
41 parent_header = Dict({})
54
42
55 def set_parent(self, parent):
43 def set_parent(self, parent):
56 """Set the parent for outbound messages."""
44 """Set the parent for outbound messages."""
57 self.parent_header = extract_header(parent)
45 self.parent_header = extract_header(parent)
58
46
59 def start_displayhook(self):
47 def start_displayhook(self):
60 self.msg = self.session.msg(u'pyout', {}, parent=self.parent_header)
48 self.msg = self.session.msg(u'pyout', {}, parent=self.parent_header)
61
49
62 def write_output_prompt(self):
50 def write_output_prompt(self):
63 """Write the output prompt."""
51 """Write the output prompt."""
64 self.msg['content']['execution_count'] = self.prompt_count
52 self.msg['content']['execution_count'] = self.prompt_count
65
53
66 def write_format_data(self, format_dict):
54 def write_format_data(self, format_dict):
67 self.msg['content']['data'] = _encode_binary(format_dict)
55 self.msg['content']['data'] = encode_images(format_dict)
68
56
69 def finish_displayhook(self):
57 def finish_displayhook(self):
70 """Finish up all displayhook activities."""
58 """Finish up all displayhook activities."""
71 sys.stdout.flush()
59 sys.stdout.flush()
72 sys.stderr.flush()
60 sys.stderr.flush()
73 self.session.send(self.pub_socket, self.msg, ident=self.topic)
61 self.session.send(self.pub_socket, self.msg, ident=self.topic)
74 self.msg = None
62 self.msg = None
75
63
@@ -1,581 +1,581 b''
1 """A ZMQ-based subclass of InteractiveShell.
1 """A ZMQ-based subclass of InteractiveShell.
2
2
3 This code is meant to ease the refactoring of the base InteractiveShell into
3 This code is meant to ease the refactoring of the base InteractiveShell into
4 something with a cleaner architecture for 2-process use, without actually
4 something with a cleaner architecture for 2-process use, without actually
5 breaking InteractiveShell itself. So we're doing something a bit ugly, where
5 breaking InteractiveShell itself. So we're doing something a bit ugly, where
6 we subclass and override what we want to fix. Once this is working well, we
6 we subclass and override what we want to fix. Once this is working well, we
7 can go back to the base class and refactor the code for a cleaner inheritance
7 can go back to the base class and refactor the code for a cleaner inheritance
8 implementation that doesn't rely on so much monkeypatching.
8 implementation that doesn't rely on so much monkeypatching.
9
9
10 But this lets us maintain a fully working IPython as we develop the new
10 But this lets us maintain a fully working IPython as we develop the new
11 machinery. This should thus be thought of as scaffolding.
11 machinery. This should thus be thought of as scaffolding.
12 """
12 """
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 from __future__ import print_function
16 from __future__ import print_function
17
17
18 # Stdlib
18 # Stdlib
19 import os
19 import os
20 import sys
20 import sys
21 import time
21 import time
22
22
23 # System library imports
23 # System library imports
24 from zmq.eventloop import ioloop
24 from zmq.eventloop import ioloop
25
25
26 # Our own
26 # Our own
27 from IPython.core.interactiveshell import (
27 from IPython.core.interactiveshell import (
28 InteractiveShell, InteractiveShellABC
28 InteractiveShell, InteractiveShellABC
29 )
29 )
30 from IPython.core import page
30 from IPython.core import page
31 from IPython.core.autocall import ZMQExitAutocall
31 from IPython.core.autocall import ZMQExitAutocall
32 from IPython.core.displaypub import DisplayPublisher
32 from IPython.core.displaypub import DisplayPublisher
33 from IPython.core.magics import MacroToEdit, CodeMagics
33 from IPython.core.magics import MacroToEdit, CodeMagics
34 from IPython.core.magic import magics_class, line_magic, Magics
34 from IPython.core.magic import magics_class, line_magic, Magics
35 from IPython.core.payloadpage import install_payload_page
35 from IPython.core.payloadpage import install_payload_page
36 from IPython.lib.kernel import (
36 from IPython.lib.kernel import (
37 get_connection_file, get_connection_info, connect_qtconsole
37 get_connection_file, get_connection_info, connect_qtconsole
38 )
38 )
39 from IPython.testing.skipdoctest import skip_doctest
39 from IPython.testing.skipdoctest import skip_doctest
40 from IPython.utils import io
40 from IPython.utils import io
41 from IPython.utils.jsonutil import json_clean
41 from IPython.utils.jsonutil import json_clean, encode_images
42 from IPython.utils.process import arg_split
42 from IPython.utils.process import arg_split
43 from IPython.utils import py3compat
43 from IPython.utils import py3compat
44 from IPython.utils.traitlets import Instance, Type, Dict, CBool, CBytes
44 from IPython.utils.traitlets import Instance, Type, Dict, CBool, CBytes
45 from IPython.utils.warn import warn, error
45 from IPython.utils.warn import warn, error
46 from IPython.zmq.displayhook import ZMQShellDisplayHook, _encode_binary
46 from IPython.zmq.displayhook import ZMQShellDisplayHook
47 from IPython.zmq.session import extract_header
47 from IPython.zmq.session import extract_header
48 from session import Session
48 from session import Session
49
49
50 #-----------------------------------------------------------------------------
50 #-----------------------------------------------------------------------------
51 # Functions and classes
51 # Functions and classes
52 #-----------------------------------------------------------------------------
52 #-----------------------------------------------------------------------------
53
53
54 class ZMQDisplayPublisher(DisplayPublisher):
54 class ZMQDisplayPublisher(DisplayPublisher):
55 """A display publisher that publishes data using a ZeroMQ PUB socket."""
55 """A display publisher that publishes data using a ZeroMQ PUB socket."""
56
56
57 session = Instance(Session)
57 session = Instance(Session)
58 pub_socket = Instance('zmq.Socket')
58 pub_socket = Instance('zmq.Socket')
59 parent_header = Dict({})
59 parent_header = Dict({})
60 topic = CBytes(b'displaypub')
60 topic = CBytes(b'displaypub')
61
61
62 def set_parent(self, parent):
62 def set_parent(self, parent):
63 """Set the parent for outbound messages."""
63 """Set the parent for outbound messages."""
64 self.parent_header = extract_header(parent)
64 self.parent_header = extract_header(parent)
65
65
66 def _flush_streams(self):
66 def _flush_streams(self):
67 """flush IO Streams prior to display"""
67 """flush IO Streams prior to display"""
68 sys.stdout.flush()
68 sys.stdout.flush()
69 sys.stderr.flush()
69 sys.stderr.flush()
70
70
71 def publish(self, source, data, metadata=None):
71 def publish(self, source, data, metadata=None):
72 self._flush_streams()
72 self._flush_streams()
73 if metadata is None:
73 if metadata is None:
74 metadata = {}
74 metadata = {}
75 self._validate_data(source, data, metadata)
75 self._validate_data(source, data, metadata)
76 content = {}
76 content = {}
77 content['source'] = source
77 content['source'] = source
78 content['data'] = _encode_binary(data)
78 content['data'] = encode_images(data)
79 content['metadata'] = metadata
79 content['metadata'] = metadata
80 self.session.send(
80 self.session.send(
81 self.pub_socket, u'display_data', json_clean(content),
81 self.pub_socket, u'display_data', json_clean(content),
82 parent=self.parent_header, ident=self.topic,
82 parent=self.parent_header, ident=self.topic,
83 )
83 )
84
84
85 def clear_output(self, stdout=True, stderr=True, other=True):
85 def clear_output(self, stdout=True, stderr=True, other=True):
86 content = dict(stdout=stdout, stderr=stderr, other=other)
86 content = dict(stdout=stdout, stderr=stderr, other=other)
87
87
88 if stdout:
88 if stdout:
89 print('\r', file=sys.stdout, end='')
89 print('\r', file=sys.stdout, end='')
90 if stderr:
90 if stderr:
91 print('\r', file=sys.stderr, end='')
91 print('\r', file=sys.stderr, end='')
92
92
93 self._flush_streams()
93 self._flush_streams()
94
94
95 self.session.send(
95 self.session.send(
96 self.pub_socket, u'clear_output', content,
96 self.pub_socket, u'clear_output', content,
97 parent=self.parent_header, ident=self.topic,
97 parent=self.parent_header, ident=self.topic,
98 )
98 )
99
99
100 @magics_class
100 @magics_class
101 class KernelMagics(Magics):
101 class KernelMagics(Magics):
102 #------------------------------------------------------------------------
102 #------------------------------------------------------------------------
103 # Magic overrides
103 # Magic overrides
104 #------------------------------------------------------------------------
104 #------------------------------------------------------------------------
105 # Once the base class stops inheriting from magic, this code needs to be
105 # Once the base class stops inheriting from magic, this code needs to be
106 # moved into a separate machinery as well. For now, at least isolate here
106 # moved into a separate machinery as well. For now, at least isolate here
107 # the magics which this class needs to implement differently from the base
107 # the magics which this class needs to implement differently from the base
108 # class, or that are unique to it.
108 # class, or that are unique to it.
109
109
110 @line_magic
110 @line_magic
111 def doctest_mode(self, parameter_s=''):
111 def doctest_mode(self, parameter_s=''):
112 """Toggle doctest mode on and off.
112 """Toggle doctest mode on and off.
113
113
114 This mode is intended to make IPython behave as much as possible like a
114 This mode is intended to make IPython behave as much as possible like a
115 plain Python shell, from the perspective of how its prompts, exceptions
115 plain Python shell, from the perspective of how its prompts, exceptions
116 and output look. This makes it easy to copy and paste parts of a
116 and output look. This makes it easy to copy and paste parts of a
117 session into doctests. It does so by:
117 session into doctests. It does so by:
118
118
119 - Changing the prompts to the classic ``>>>`` ones.
119 - Changing the prompts to the classic ``>>>`` ones.
120 - Changing the exception reporting mode to 'Plain'.
120 - Changing the exception reporting mode to 'Plain'.
121 - Disabling pretty-printing of output.
121 - Disabling pretty-printing of output.
122
122
123 Note that IPython also supports the pasting of code snippets that have
123 Note that IPython also supports the pasting of code snippets that have
124 leading '>>>' and '...' prompts in them. This means that you can paste
124 leading '>>>' and '...' prompts in them. This means that you can paste
125 doctests from files or docstrings (even if they have leading
125 doctests from files or docstrings (even if they have leading
126 whitespace), and the code will execute correctly. You can then use
126 whitespace), and the code will execute correctly. You can then use
127 '%history -t' to see the translated history; this will give you the
127 '%history -t' to see the translated history; this will give you the
128 input after removal of all the leading prompts and whitespace, which
128 input after removal of all the leading prompts and whitespace, which
129 can be pasted back into an editor.
129 can be pasted back into an editor.
130
130
131 With these features, you can switch into this mode easily whenever you
131 With these features, you can switch into this mode easily whenever you
132 need to do testing and changes to doctests, without having to leave
132 need to do testing and changes to doctests, without having to leave
133 your existing IPython session.
133 your existing IPython session.
134 """
134 """
135
135
136 from IPython.utils.ipstruct import Struct
136 from IPython.utils.ipstruct import Struct
137
137
138 # Shorthands
138 # Shorthands
139 shell = self.shell
139 shell = self.shell
140 disp_formatter = self.shell.display_formatter
140 disp_formatter = self.shell.display_formatter
141 ptformatter = disp_formatter.formatters['text/plain']
141 ptformatter = disp_formatter.formatters['text/plain']
142 # dstore is a data store kept in the instance metadata bag to track any
142 # dstore is a data store kept in the instance metadata bag to track any
143 # changes we make, so we can undo them later.
143 # changes we make, so we can undo them later.
144 dstore = shell.meta.setdefault('doctest_mode', Struct())
144 dstore = shell.meta.setdefault('doctest_mode', Struct())
145 save_dstore = dstore.setdefault
145 save_dstore = dstore.setdefault
146
146
147 # save a few values we'll need to recover later
147 # save a few values we'll need to recover later
148 mode = save_dstore('mode', False)
148 mode = save_dstore('mode', False)
149 save_dstore('rc_pprint', ptformatter.pprint)
149 save_dstore('rc_pprint', ptformatter.pprint)
150 save_dstore('rc_plain_text_only',disp_formatter.plain_text_only)
150 save_dstore('rc_plain_text_only',disp_formatter.plain_text_only)
151 save_dstore('xmode', shell.InteractiveTB.mode)
151 save_dstore('xmode', shell.InteractiveTB.mode)
152
152
153 if mode == False:
153 if mode == False:
154 # turn on
154 # turn on
155 ptformatter.pprint = False
155 ptformatter.pprint = False
156 disp_formatter.plain_text_only = True
156 disp_formatter.plain_text_only = True
157 shell.magic('xmode Plain')
157 shell.magic('xmode Plain')
158 else:
158 else:
159 # turn off
159 # turn off
160 ptformatter.pprint = dstore.rc_pprint
160 ptformatter.pprint = dstore.rc_pprint
161 disp_formatter.plain_text_only = dstore.rc_plain_text_only
161 disp_formatter.plain_text_only = dstore.rc_plain_text_only
162 shell.magic("xmode " + dstore.xmode)
162 shell.magic("xmode " + dstore.xmode)
163
163
164 # Store new mode and inform on console
164 # Store new mode and inform on console
165 dstore.mode = bool(1-int(mode))
165 dstore.mode = bool(1-int(mode))
166 mode_label = ['OFF','ON'][dstore.mode]
166 mode_label = ['OFF','ON'][dstore.mode]
167 print('Doctest mode is:', mode_label)
167 print('Doctest mode is:', mode_label)
168
168
169 # Send the payload back so that clients can modify their prompt display
169 # Send the payload back so that clients can modify their prompt display
170 payload = dict(
170 payload = dict(
171 source='IPython.zmq.zmqshell.ZMQInteractiveShell.doctest_mode',
171 source='IPython.zmq.zmqshell.ZMQInteractiveShell.doctest_mode',
172 mode=dstore.mode)
172 mode=dstore.mode)
173 shell.payload_manager.write_payload(payload)
173 shell.payload_manager.write_payload(payload)
174
174
175
175
176 _find_edit_target = CodeMagics._find_edit_target
176 _find_edit_target = CodeMagics._find_edit_target
177
177
178 @skip_doctest
178 @skip_doctest
179 @line_magic
179 @line_magic
180 def edit(self, parameter_s='', last_call=['','']):
180 def edit(self, parameter_s='', last_call=['','']):
181 """Bring up an editor and execute the resulting code.
181 """Bring up an editor and execute the resulting code.
182
182
183 Usage:
183 Usage:
184 %edit [options] [args]
184 %edit [options] [args]
185
185
186 %edit runs an external text editor. You will need to set the command for
186 %edit runs an external text editor. You will need to set the command for
187 this editor via the ``TerminalInteractiveShell.editor`` option in your
187 this editor via the ``TerminalInteractiveShell.editor`` option in your
188 configuration file before it will work.
188 configuration file before it will work.
189
189
190 This command allows you to conveniently edit multi-line code right in
190 This command allows you to conveniently edit multi-line code right in
191 your IPython session.
191 your IPython session.
192
192
193 If called without arguments, %edit opens up an empty editor with a
193 If called without arguments, %edit opens up an empty editor with a
194 temporary file and will execute the contents of this file when you
194 temporary file and will execute the contents of this file when you
195 close it (don't forget to save it!).
195 close it (don't forget to save it!).
196
196
197
197
198 Options:
198 Options:
199
199
200 -n <number>: open the editor at a specified line number. By default,
200 -n <number>: open the editor at a specified line number. By default,
201 the IPython editor hook uses the unix syntax 'editor +N filename', but
201 the IPython editor hook uses the unix syntax 'editor +N filename', but
202 you can configure this by providing your own modified hook if your
202 you can configure this by providing your own modified hook if your
203 favorite editor supports line-number specifications with a different
203 favorite editor supports line-number specifications with a different
204 syntax.
204 syntax.
205
205
206 -p: this will call the editor with the same data as the previous time
206 -p: this will call the editor with the same data as the previous time
207 it was used, regardless of how long ago (in your current session) it
207 it was used, regardless of how long ago (in your current session) it
208 was.
208 was.
209
209
210 -r: use 'raw' input. This option only applies to input taken from the
210 -r: use 'raw' input. This option only applies to input taken from the
211 user's history. By default, the 'processed' history is used, so that
211 user's history. By default, the 'processed' history is used, so that
212 magics are loaded in their transformed version to valid Python. If
212 magics are loaded in their transformed version to valid Python. If
213 this option is given, the raw input as typed as the command line is
213 this option is given, the raw input as typed as the command line is
214 used instead. When you exit the editor, it will be executed by
214 used instead. When you exit the editor, it will be executed by
215 IPython's own processor.
215 IPython's own processor.
216
216
217 -x: do not execute the edited code immediately upon exit. This is
217 -x: do not execute the edited code immediately upon exit. This is
218 mainly useful if you are editing programs which need to be called with
218 mainly useful if you are editing programs which need to be called with
219 command line arguments, which you can then do using %run.
219 command line arguments, which you can then do using %run.
220
220
221
221
222 Arguments:
222 Arguments:
223
223
224 If arguments are given, the following possibilites exist:
224 If arguments are given, the following possibilites exist:
225
225
226 - The arguments are numbers or pairs of colon-separated numbers (like
226 - The arguments are numbers or pairs of colon-separated numbers (like
227 1 4:8 9). These are interpreted as lines of previous input to be
227 1 4:8 9). These are interpreted as lines of previous input to be
228 loaded into the editor. The syntax is the same of the %macro command.
228 loaded into the editor. The syntax is the same of the %macro command.
229
229
230 - If the argument doesn't start with a number, it is evaluated as a
230 - If the argument doesn't start with a number, it is evaluated as a
231 variable and its contents loaded into the editor. You can thus edit
231 variable and its contents loaded into the editor. You can thus edit
232 any string which contains python code (including the result of
232 any string which contains python code (including the result of
233 previous edits).
233 previous edits).
234
234
235 - If the argument is the name of an object (other than a string),
235 - If the argument is the name of an object (other than a string),
236 IPython will try to locate the file where it was defined and open the
236 IPython will try to locate the file where it was defined and open the
237 editor at the point where it is defined. You can use `%edit function`
237 editor at the point where it is defined. You can use `%edit function`
238 to load an editor exactly at the point where 'function' is defined,
238 to load an editor exactly at the point where 'function' is defined,
239 edit it and have the file be executed automatically.
239 edit it and have the file be executed automatically.
240
240
241 If the object is a macro (see %macro for details), this opens up your
241 If the object is a macro (see %macro for details), this opens up your
242 specified editor with a temporary file containing the macro's data.
242 specified editor with a temporary file containing the macro's data.
243 Upon exit, the macro is reloaded with the contents of the file.
243 Upon exit, the macro is reloaded with the contents of the file.
244
244
245 Note: opening at an exact line is only supported under Unix, and some
245 Note: opening at an exact line is only supported under Unix, and some
246 editors (like kedit and gedit up to Gnome 2.8) do not understand the
246 editors (like kedit and gedit up to Gnome 2.8) do not understand the
247 '+NUMBER' parameter necessary for this feature. Good editors like
247 '+NUMBER' parameter necessary for this feature. Good editors like
248 (X)Emacs, vi, jed, pico and joe all do.
248 (X)Emacs, vi, jed, pico and joe all do.
249
249
250 - If the argument is not found as a variable, IPython will look for a
250 - If the argument is not found as a variable, IPython will look for a
251 file with that name (adding .py if necessary) and load it into the
251 file with that name (adding .py if necessary) and load it into the
252 editor. It will execute its contents with execfile() when you exit,
252 editor. It will execute its contents with execfile() when you exit,
253 loading any code in the file into your interactive namespace.
253 loading any code in the file into your interactive namespace.
254
254
255 After executing your code, %edit will return as output the code you
255 After executing your code, %edit will return as output the code you
256 typed in the editor (except when it was an existing file). This way
256 typed in the editor (except when it was an existing file). This way
257 you can reload the code in further invocations of %edit as a variable,
257 you can reload the code in further invocations of %edit as a variable,
258 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
258 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
259 the output.
259 the output.
260
260
261 Note that %edit is also available through the alias %ed.
261 Note that %edit is also available through the alias %ed.
262
262
263 This is an example of creating a simple function inside the editor and
263 This is an example of creating a simple function inside the editor and
264 then modifying it. First, start up the editor:
264 then modifying it. First, start up the editor:
265
265
266 In [1]: ed
266 In [1]: ed
267 Editing... done. Executing edited code...
267 Editing... done. Executing edited code...
268 Out[1]: 'def foo():n print "foo() was defined in an editing session"n'
268 Out[1]: 'def foo():n print "foo() was defined in an editing session"n'
269
269
270 We can then call the function foo():
270 We can then call the function foo():
271
271
272 In [2]: foo()
272 In [2]: foo()
273 foo() was defined in an editing session
273 foo() was defined in an editing session
274
274
275 Now we edit foo. IPython automatically loads the editor with the
275 Now we edit foo. IPython automatically loads the editor with the
276 (temporary) file where foo() was previously defined:
276 (temporary) file where foo() was previously defined:
277
277
278 In [3]: ed foo
278 In [3]: ed foo
279 Editing... done. Executing edited code...
279 Editing... done. Executing edited code...
280
280
281 And if we call foo() again we get the modified version:
281 And if we call foo() again we get the modified version:
282
282
283 In [4]: foo()
283 In [4]: foo()
284 foo() has now been changed!
284 foo() has now been changed!
285
285
286 Here is an example of how to edit a code snippet successive
286 Here is an example of how to edit a code snippet successive
287 times. First we call the editor:
287 times. First we call the editor:
288
288
289 In [5]: ed
289 In [5]: ed
290 Editing... done. Executing edited code...
290 Editing... done. Executing edited code...
291 hello
291 hello
292 Out[5]: "print 'hello'n"
292 Out[5]: "print 'hello'n"
293
293
294 Now we call it again with the previous output (stored in _):
294 Now we call it again with the previous output (stored in _):
295
295
296 In [6]: ed _
296 In [6]: ed _
297 Editing... done. Executing edited code...
297 Editing... done. Executing edited code...
298 hello world
298 hello world
299 Out[6]: "print 'hello world'n"
299 Out[6]: "print 'hello world'n"
300
300
301 Now we call it with the output #8 (stored in _8, also as Out[8]):
301 Now we call it with the output #8 (stored in _8, also as Out[8]):
302
302
303 In [7]: ed _8
303 In [7]: ed _8
304 Editing... done. Executing edited code...
304 Editing... done. Executing edited code...
305 hello again
305 hello again
306 Out[7]: "print 'hello again'n"
306 Out[7]: "print 'hello again'n"
307 """
307 """
308
308
309 opts,args = self.parse_options(parameter_s,'prn:')
309 opts,args = self.parse_options(parameter_s,'prn:')
310
310
311 try:
311 try:
312 filename, lineno, _ = CodeMagics._find_edit_target(self.shell, args, opts, last_call)
312 filename, lineno, _ = CodeMagics._find_edit_target(self.shell, args, opts, last_call)
313 except MacroToEdit as e:
313 except MacroToEdit as e:
314 # TODO: Implement macro editing over 2 processes.
314 # TODO: Implement macro editing over 2 processes.
315 print("Macro editing not yet implemented in 2-process model.")
315 print("Macro editing not yet implemented in 2-process model.")
316 return
316 return
317
317
318 # Make sure we send to the client an absolute path, in case the working
318 # Make sure we send to the client an absolute path, in case the working
319 # directory of client and kernel don't match
319 # directory of client and kernel don't match
320 filename = os.path.abspath(filename)
320 filename = os.path.abspath(filename)
321
321
322 payload = {
322 payload = {
323 'source' : 'IPython.zmq.zmqshell.ZMQInteractiveShell.edit_magic',
323 'source' : 'IPython.zmq.zmqshell.ZMQInteractiveShell.edit_magic',
324 'filename' : filename,
324 'filename' : filename,
325 'line_number' : lineno
325 'line_number' : lineno
326 }
326 }
327 self.shell.payload_manager.write_payload(payload)
327 self.shell.payload_manager.write_payload(payload)
328
328
329 # A few magics that are adapted to the specifics of using pexpect and a
329 # A few magics that are adapted to the specifics of using pexpect and a
330 # remote terminal
330 # remote terminal
331
331
332 @line_magic
332 @line_magic
333 def clear(self, arg_s):
333 def clear(self, arg_s):
334 """Clear the terminal."""
334 """Clear the terminal."""
335 if os.name == 'posix':
335 if os.name == 'posix':
336 self.shell.system("clear")
336 self.shell.system("clear")
337 else:
337 else:
338 self.shell.system("cls")
338 self.shell.system("cls")
339
339
340 if os.name == 'nt':
340 if os.name == 'nt':
341 # This is the usual name in windows
341 # This is the usual name in windows
342 cls = line_magic('cls')(clear)
342 cls = line_magic('cls')(clear)
343
343
344 # Terminal pagers won't work over pexpect, but we do have our own pager
344 # Terminal pagers won't work over pexpect, but we do have our own pager
345
345
346 @line_magic
346 @line_magic
347 def less(self, arg_s):
347 def less(self, arg_s):
348 """Show a file through the pager.
348 """Show a file through the pager.
349
349
350 Files ending in .py are syntax-highlighted."""
350 Files ending in .py are syntax-highlighted."""
351 cont = open(arg_s).read()
351 cont = open(arg_s).read()
352 if arg_s.endswith('.py'):
352 if arg_s.endswith('.py'):
353 cont = self.shell.pycolorize(cont)
353 cont = self.shell.pycolorize(cont)
354 page.page(cont)
354 page.page(cont)
355
355
356 more = line_magic('more')(less)
356 more = line_magic('more')(less)
357
357
358 # Man calls a pager, so we also need to redefine it
358 # Man calls a pager, so we also need to redefine it
359 if os.name == 'posix':
359 if os.name == 'posix':
360 @line_magic
360 @line_magic
361 def man(self, arg_s):
361 def man(self, arg_s):
362 """Find the man page for the given command and display in pager."""
362 """Find the man page for the given command and display in pager."""
363 page.page(self.shell.getoutput('man %s | col -b' % arg_s,
363 page.page(self.shell.getoutput('man %s | col -b' % arg_s,
364 split=False))
364 split=False))
365
365
366 @line_magic
366 @line_magic
367 def connect_info(self, arg_s):
367 def connect_info(self, arg_s):
368 """Print information for connecting other clients to this kernel
368 """Print information for connecting other clients to this kernel
369
369
370 It will print the contents of this session's connection file, as well as
370 It will print the contents of this session's connection file, as well as
371 shortcuts for local clients.
371 shortcuts for local clients.
372
372
373 In the simplest case, when called from the most recently launched kernel,
373 In the simplest case, when called from the most recently launched kernel,
374 secondary clients can be connected, simply with:
374 secondary clients can be connected, simply with:
375
375
376 $> ipython <app> --existing
376 $> ipython <app> --existing
377
377
378 """
378 """
379
379
380 from IPython.core.application import BaseIPythonApplication as BaseIPApp
380 from IPython.core.application import BaseIPythonApplication as BaseIPApp
381
381
382 if BaseIPApp.initialized():
382 if BaseIPApp.initialized():
383 app = BaseIPApp.instance()
383 app = BaseIPApp.instance()
384 security_dir = app.profile_dir.security_dir
384 security_dir = app.profile_dir.security_dir
385 profile = app.profile
385 profile = app.profile
386 else:
386 else:
387 profile = 'default'
387 profile = 'default'
388 security_dir = ''
388 security_dir = ''
389
389
390 try:
390 try:
391 connection_file = get_connection_file()
391 connection_file = get_connection_file()
392 info = get_connection_info(unpack=False)
392 info = get_connection_info(unpack=False)
393 except Exception as e:
393 except Exception as e:
394 error("Could not get connection info: %r" % e)
394 error("Could not get connection info: %r" % e)
395 return
395 return
396
396
397 # add profile flag for non-default profile
397 # add profile flag for non-default profile
398 profile_flag = "--profile %s" % profile if profile != 'default' else ""
398 profile_flag = "--profile %s" % profile if profile != 'default' else ""
399
399
400 # if it's in the security dir, truncate to basename
400 # if it's in the security dir, truncate to basename
401 if security_dir == os.path.dirname(connection_file):
401 if security_dir == os.path.dirname(connection_file):
402 connection_file = os.path.basename(connection_file)
402 connection_file = os.path.basename(connection_file)
403
403
404
404
405 print (info + '\n')
405 print (info + '\n')
406 print ("Paste the above JSON into a file, and connect with:\n"
406 print ("Paste the above JSON into a file, and connect with:\n"
407 " $> ipython <app> --existing <file>\n"
407 " $> ipython <app> --existing <file>\n"
408 "or, if you are local, you can connect with just:\n"
408 "or, if you are local, you can connect with just:\n"
409 " $> ipython <app> --existing {0} {1}\n"
409 " $> ipython <app> --existing {0} {1}\n"
410 "or even just:\n"
410 "or even just:\n"
411 " $> ipython <app> --existing {1}\n"
411 " $> ipython <app> --existing {1}\n"
412 "if this is the most recent IPython session you have started.".format(
412 "if this is the most recent IPython session you have started.".format(
413 connection_file, profile_flag
413 connection_file, profile_flag
414 )
414 )
415 )
415 )
416
416
417 @line_magic
417 @line_magic
418 def qtconsole(self, arg_s):
418 def qtconsole(self, arg_s):
419 """Open a qtconsole connected to this kernel.
419 """Open a qtconsole connected to this kernel.
420
420
421 Useful for connecting a qtconsole to running notebooks, for better
421 Useful for connecting a qtconsole to running notebooks, for better
422 debugging.
422 debugging.
423 """
423 """
424
424
425 # %qtconsole should imply bind_kernel for engines:
425 # %qtconsole should imply bind_kernel for engines:
426 try:
426 try:
427 from IPython.parallel import bind_kernel
427 from IPython.parallel import bind_kernel
428 except ImportError:
428 except ImportError:
429 # technically possible, because parallel has higher pyzmq min-version
429 # technically possible, because parallel has higher pyzmq min-version
430 pass
430 pass
431 else:
431 else:
432 bind_kernel()
432 bind_kernel()
433
433
434 try:
434 try:
435 p = connect_qtconsole(argv=arg_split(arg_s, os.name=='posix'))
435 p = connect_qtconsole(argv=arg_split(arg_s, os.name=='posix'))
436 except Exception as e:
436 except Exception as e:
437 error("Could not start qtconsole: %r" % e)
437 error("Could not start qtconsole: %r" % e)
438 return
438 return
439
439
440 def safe_unicode(e):
440 def safe_unicode(e):
441 """unicode(e) with various fallbacks. Used for exceptions, which may not be
441 """unicode(e) with various fallbacks. Used for exceptions, which may not be
442 safe to call unicode() on.
442 safe to call unicode() on.
443 """
443 """
444 try:
444 try:
445 return unicode(e)
445 return unicode(e)
446 except UnicodeError:
446 except UnicodeError:
447 pass
447 pass
448
448
449 try:
449 try:
450 return py3compat.str_to_unicode(str(e))
450 return py3compat.str_to_unicode(str(e))
451 except UnicodeError:
451 except UnicodeError:
452 pass
452 pass
453
453
454 try:
454 try:
455 return py3compat.str_to_unicode(repr(e))
455 return py3compat.str_to_unicode(repr(e))
456 except UnicodeError:
456 except UnicodeError:
457 pass
457 pass
458
458
459 return u'Unrecoverably corrupt evalue'
459 return u'Unrecoverably corrupt evalue'
460
460
461
461
462 class ZMQInteractiveShell(InteractiveShell):
462 class ZMQInteractiveShell(InteractiveShell):
463 """A subclass of InteractiveShell for ZMQ."""
463 """A subclass of InteractiveShell for ZMQ."""
464
464
465 displayhook_class = Type(ZMQShellDisplayHook)
465 displayhook_class = Type(ZMQShellDisplayHook)
466 display_pub_class = Type(ZMQDisplayPublisher)
466 display_pub_class = Type(ZMQDisplayPublisher)
467
467
468 # Override the traitlet in the parent class, because there's no point using
468 # Override the traitlet in the parent class, because there's no point using
469 # readline for the kernel. Can be removed when the readline code is moved
469 # readline for the kernel. Can be removed when the readline code is moved
470 # to the terminal frontend.
470 # to the terminal frontend.
471 colors_force = CBool(True)
471 colors_force = CBool(True)
472 readline_use = CBool(False)
472 readline_use = CBool(False)
473 # autoindent has no meaning in a zmqshell, and attempting to enable it
473 # autoindent has no meaning in a zmqshell, and attempting to enable it
474 # will print a warning in the absence of readline.
474 # will print a warning in the absence of readline.
475 autoindent = CBool(False)
475 autoindent = CBool(False)
476
476
477 exiter = Instance(ZMQExitAutocall)
477 exiter = Instance(ZMQExitAutocall)
478 def _exiter_default(self):
478 def _exiter_default(self):
479 return ZMQExitAutocall(self)
479 return ZMQExitAutocall(self)
480
480
481 def _exit_now_changed(self, name, old, new):
481 def _exit_now_changed(self, name, old, new):
482 """stop eventloop when exit_now fires"""
482 """stop eventloop when exit_now fires"""
483 if new:
483 if new:
484 loop = ioloop.IOLoop.instance()
484 loop = ioloop.IOLoop.instance()
485 loop.add_timeout(time.time()+0.1, loop.stop)
485 loop.add_timeout(time.time()+0.1, loop.stop)
486
486
487 keepkernel_on_exit = None
487 keepkernel_on_exit = None
488
488
489 # Over ZeroMQ, GUI control isn't done with PyOS_InputHook as there is no
489 # Over ZeroMQ, GUI control isn't done with PyOS_InputHook as there is no
490 # interactive input being read; we provide event loop support in ipkernel
490 # interactive input being read; we provide event loop support in ipkernel
491 from .eventloops import enable_gui
491 from .eventloops import enable_gui
492 enable_gui = staticmethod(enable_gui)
492 enable_gui = staticmethod(enable_gui)
493
493
494 def init_environment(self):
494 def init_environment(self):
495 """Configure the user's environment.
495 """Configure the user's environment.
496
496
497 """
497 """
498 env = os.environ
498 env = os.environ
499 # These two ensure 'ls' produces nice coloring on BSD-derived systems
499 # These two ensure 'ls' produces nice coloring on BSD-derived systems
500 env['TERM'] = 'xterm-color'
500 env['TERM'] = 'xterm-color'
501 env['CLICOLOR'] = '1'
501 env['CLICOLOR'] = '1'
502 # Since normal pagers don't work at all (over pexpect we don't have
502 # Since normal pagers don't work at all (over pexpect we don't have
503 # single-key control of the subprocess), try to disable paging in
503 # single-key control of the subprocess), try to disable paging in
504 # subprocesses as much as possible.
504 # subprocesses as much as possible.
505 env['PAGER'] = 'cat'
505 env['PAGER'] = 'cat'
506 env['GIT_PAGER'] = 'cat'
506 env['GIT_PAGER'] = 'cat'
507
507
508 # And install the payload version of page.
508 # And install the payload version of page.
509 install_payload_page()
509 install_payload_page()
510
510
511 def auto_rewrite_input(self, cmd):
511 def auto_rewrite_input(self, cmd):
512 """Called to show the auto-rewritten input for autocall and friends.
512 """Called to show the auto-rewritten input for autocall and friends.
513
513
514 FIXME: this payload is currently not correctly processed by the
514 FIXME: this payload is currently not correctly processed by the
515 frontend.
515 frontend.
516 """
516 """
517 new = self.prompt_manager.render('rewrite') + cmd
517 new = self.prompt_manager.render('rewrite') + cmd
518 payload = dict(
518 payload = dict(
519 source='IPython.zmq.zmqshell.ZMQInteractiveShell.auto_rewrite_input',
519 source='IPython.zmq.zmqshell.ZMQInteractiveShell.auto_rewrite_input',
520 transformed_input=new,
520 transformed_input=new,
521 )
521 )
522 self.payload_manager.write_payload(payload)
522 self.payload_manager.write_payload(payload)
523
523
524 def ask_exit(self):
524 def ask_exit(self):
525 """Engage the exit actions."""
525 """Engage the exit actions."""
526 self.exit_now = True
526 self.exit_now = True
527 payload = dict(
527 payload = dict(
528 source='IPython.zmq.zmqshell.ZMQInteractiveShell.ask_exit',
528 source='IPython.zmq.zmqshell.ZMQInteractiveShell.ask_exit',
529 exit=True,
529 exit=True,
530 keepkernel=self.keepkernel_on_exit,
530 keepkernel=self.keepkernel_on_exit,
531 )
531 )
532 self.payload_manager.write_payload(payload)
532 self.payload_manager.write_payload(payload)
533
533
534 def _showtraceback(self, etype, evalue, stb):
534 def _showtraceback(self, etype, evalue, stb):
535
535
536 exc_content = {
536 exc_content = {
537 u'traceback' : stb,
537 u'traceback' : stb,
538 u'ename' : unicode(etype.__name__),
538 u'ename' : unicode(etype.__name__),
539 u'evalue' : safe_unicode(evalue)
539 u'evalue' : safe_unicode(evalue)
540 }
540 }
541
541
542 dh = self.displayhook
542 dh = self.displayhook
543 # Send exception info over pub socket for other clients than the caller
543 # Send exception info over pub socket for other clients than the caller
544 # to pick up
544 # to pick up
545 topic = None
545 topic = None
546 if dh.topic:
546 if dh.topic:
547 topic = dh.topic.replace(b'pyout', b'pyerr')
547 topic = dh.topic.replace(b'pyout', b'pyerr')
548
548
549 exc_msg = dh.session.send(dh.pub_socket, u'pyerr', json_clean(exc_content), dh.parent_header, ident=topic)
549 exc_msg = dh.session.send(dh.pub_socket, u'pyerr', json_clean(exc_content), dh.parent_header, ident=topic)
550
550
551 # FIXME - Hack: store exception info in shell object. Right now, the
551 # FIXME - Hack: store exception info in shell object. Right now, the
552 # caller is reading this info after the fact, we need to fix this logic
552 # caller is reading this info after the fact, we need to fix this logic
553 # to remove this hack. Even uglier, we need to store the error status
553 # to remove this hack. Even uglier, we need to store the error status
554 # here, because in the main loop, the logic that sets it is being
554 # here, because in the main loop, the logic that sets it is being
555 # skipped because runlines swallows the exceptions.
555 # skipped because runlines swallows the exceptions.
556 exc_content[u'status'] = u'error'
556 exc_content[u'status'] = u'error'
557 self._reply_content = exc_content
557 self._reply_content = exc_content
558 # /FIXME
558 # /FIXME
559
559
560 return exc_content
560 return exc_content
561
561
562 def set_next_input(self, text):
562 def set_next_input(self, text):
563 """Send the specified text to the frontend to be presented at the next
563 """Send the specified text to the frontend to be presented at the next
564 input cell."""
564 input cell."""
565 payload = dict(
565 payload = dict(
566 source='IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input',
566 source='IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input',
567 text=text
567 text=text
568 )
568 )
569 self.payload_manager.write_payload(payload)
569 self.payload_manager.write_payload(payload)
570
570
571 #-------------------------------------------------------------------------
571 #-------------------------------------------------------------------------
572 # Things related to magics
572 # Things related to magics
573 #-------------------------------------------------------------------------
573 #-------------------------------------------------------------------------
574
574
575 def init_magics(self):
575 def init_magics(self):
576 super(ZMQInteractiveShell, self).init_magics()
576 super(ZMQInteractiveShell, self).init_magics()
577 self.register_magics(KernelMagics)
577 self.register_magics(KernelMagics)
578
578
579
579
580
580
581 InteractiveShellABC.register(ZMQInteractiveShell)
581 InteractiveShellABC.register(ZMQInteractiveShell)
General Comments 0
You need to be logged in to leave comments. Login now