##// END OF EJS Templates
only complete on current line...
MinRK -
Show More
@@ -1,224 +1,228 b''
1 # coding: utf-8
1 # coding: utf-8
2 """test the IPython Kernel"""
2 """test the IPython Kernel"""
3
3
4 #-------------------------------------------------------------------------------
4 # Copyright (c) IPython Development Team.
5 # Copyright (C) 2013 The IPython Development Team
5 # Distributed under the terms of the Modified BSD License.
6 #
7 # Distributed under the terms of the BSD License. The full license is in
8 # the file COPYING, distributed as part of this software.
9 #-------------------------------------------------------------------------------
10
11 #-------------------------------------------------------------------------------
12 # Imports
13 #-------------------------------------------------------------------------------
14
6
15 import io
7 import io
16 import os.path
8 import os.path
17 import sys
9 import sys
18
10
19 import nose.tools as nt
11 import nose.tools as nt
20
12
21 from IPython.testing import decorators as dec, tools as tt
13 from IPython.testing import decorators as dec, tools as tt
22 from IPython.utils import py3compat
14 from IPython.utils import py3compat
23 from IPython.utils.path import locate_profile
15 from IPython.utils.path import locate_profile
24 from IPython.utils.tempdir import TemporaryDirectory
16 from IPython.utils.tempdir import TemporaryDirectory
25
17
26 from .utils import (new_kernel, kernel, TIMEOUT, assemble_output, execute,
18 from .utils import (new_kernel, kernel, TIMEOUT, assemble_output, execute,
27 flush_channels, wait_for_idle)
19 flush_channels, wait_for_idle)
28
20
29 #-------------------------------------------------------------------------------
30 # Tests
31 #-------------------------------------------------------------------------------
32
33
21
34 def _check_mp_mode(kc, expected=False, stream="stdout"):
22 def _check_mp_mode(kc, expected=False, stream="stdout"):
35 execute(kc=kc, code="import sys")
23 execute(kc=kc, code="import sys")
36 flush_channels(kc)
24 flush_channels(kc)
37 msg_id, content = execute(kc=kc, code="print (sys.%s._check_mp_mode())" % stream)
25 msg_id, content = execute(kc=kc, code="print (sys.%s._check_mp_mode())" % stream)
38 stdout, stderr = assemble_output(kc.iopub_channel)
26 stdout, stderr = assemble_output(kc.iopub_channel)
39 nt.assert_equal(eval(stdout.strip()), expected)
27 nt.assert_equal(eval(stdout.strip()), expected)
40
28
41
29
42 # printing tests
30 # printing tests
43
31
44 def test_simple_print():
32 def test_simple_print():
45 """simple print statement in kernel"""
33 """simple print statement in kernel"""
46 with kernel() as kc:
34 with kernel() as kc:
47 iopub = kc.iopub_channel
35 iopub = kc.iopub_channel
48 msg_id, content = execute(kc=kc, code="print ('hi')")
36 msg_id, content = execute(kc=kc, code="print ('hi')")
49 stdout, stderr = assemble_output(iopub)
37 stdout, stderr = assemble_output(iopub)
50 nt.assert_equal(stdout, 'hi\n')
38 nt.assert_equal(stdout, 'hi\n')
51 nt.assert_equal(stderr, '')
39 nt.assert_equal(stderr, '')
52 _check_mp_mode(kc, expected=False)
40 _check_mp_mode(kc, expected=False)
53
41
54
42
55 def test_sys_path():
43 def test_sys_path():
56 """test that sys.path doesn't get messed up by default"""
44 """test that sys.path doesn't get messed up by default"""
57 with kernel() as kc:
45 with kernel() as kc:
58 msg_id, content = execute(kc=kc, code="import sys; print (repr(sys.path[0]))")
46 msg_id, content = execute(kc=kc, code="import sys; print (repr(sys.path[0]))")
59 stdout, stderr = assemble_output(kc.iopub_channel)
47 stdout, stderr = assemble_output(kc.iopub_channel)
60 nt.assert_equal(stdout, "''\n")
48 nt.assert_equal(stdout, "''\n")
61
49
62 def test_sys_path_profile_dir():
50 def test_sys_path_profile_dir():
63 """test that sys.path doesn't get messed up when `--profile-dir` is specified"""
51 """test that sys.path doesn't get messed up when `--profile-dir` is specified"""
64
52
65 with new_kernel(['--profile-dir', locate_profile('default')]) as kc:
53 with new_kernel(['--profile-dir', locate_profile('default')]) as kc:
66 msg_id, content = execute(kc=kc, code="import sys; print (repr(sys.path[0]))")
54 msg_id, content = execute(kc=kc, code="import sys; print (repr(sys.path[0]))")
67 stdout, stderr = assemble_output(kc.iopub_channel)
55 stdout, stderr = assemble_output(kc.iopub_channel)
68 nt.assert_equal(stdout, "''\n")
56 nt.assert_equal(stdout, "''\n")
69
57
70 @dec.knownfailureif(sys.platform == 'win32', "subprocess prints fail on Windows")
58 @dec.knownfailureif(sys.platform == 'win32', "subprocess prints fail on Windows")
71 def test_subprocess_print():
59 def test_subprocess_print():
72 """printing from forked mp.Process"""
60 """printing from forked mp.Process"""
73 with new_kernel() as kc:
61 with new_kernel() as kc:
74 iopub = kc.iopub_channel
62 iopub = kc.iopub_channel
75
63
76 _check_mp_mode(kc, expected=False)
64 _check_mp_mode(kc, expected=False)
77 flush_channels(kc)
65 flush_channels(kc)
78 np = 5
66 np = 5
79 code = '\n'.join([
67 code = '\n'.join([
80 "from __future__ import print_function",
68 "from __future__ import print_function",
81 "import multiprocessing as mp",
69 "import multiprocessing as mp",
82 "pool = [mp.Process(target=print, args=('hello', i,)) for i in range(%i)]" % np,
70 "pool = [mp.Process(target=print, args=('hello', i,)) for i in range(%i)]" % np,
83 "for p in pool: p.start()",
71 "for p in pool: p.start()",
84 "for p in pool: p.join()"
72 "for p in pool: p.join()"
85 ])
73 ])
86
74
87 expected = '\n'.join([
75 expected = '\n'.join([
88 "hello %s" % i for i in range(np)
76 "hello %s" % i for i in range(np)
89 ]) + '\n'
77 ]) + '\n'
90
78
91 msg_id, content = execute(kc=kc, code=code)
79 msg_id, content = execute(kc=kc, code=code)
92 stdout, stderr = assemble_output(iopub)
80 stdout, stderr = assemble_output(iopub)
93 nt.assert_equal(stdout.count("hello"), np, stdout)
81 nt.assert_equal(stdout.count("hello"), np, stdout)
94 for n in range(np):
82 for n in range(np):
95 nt.assert_equal(stdout.count(str(n)), 1, stdout)
83 nt.assert_equal(stdout.count(str(n)), 1, stdout)
96 nt.assert_equal(stderr, '')
84 nt.assert_equal(stderr, '')
97 _check_mp_mode(kc, expected=False)
85 _check_mp_mode(kc, expected=False)
98 _check_mp_mode(kc, expected=False, stream="stderr")
86 _check_mp_mode(kc, expected=False, stream="stderr")
99
87
100
88
101 def test_subprocess_noprint():
89 def test_subprocess_noprint():
102 """mp.Process without print doesn't trigger iostream mp_mode"""
90 """mp.Process without print doesn't trigger iostream mp_mode"""
103 with kernel() as kc:
91 with kernel() as kc:
104 iopub = kc.iopub_channel
92 iopub = kc.iopub_channel
105
93
106 np = 5
94 np = 5
107 code = '\n'.join([
95 code = '\n'.join([
108 "import multiprocessing as mp",
96 "import multiprocessing as mp",
109 "pool = [mp.Process(target=range, args=(i,)) for i in range(%i)]" % np,
97 "pool = [mp.Process(target=range, args=(i,)) for i in range(%i)]" % np,
110 "for p in pool: p.start()",
98 "for p in pool: p.start()",
111 "for p in pool: p.join()"
99 "for p in pool: p.join()"
112 ])
100 ])
113
101
114 msg_id, content = execute(kc=kc, code=code)
102 msg_id, content = execute(kc=kc, code=code)
115 stdout, stderr = assemble_output(iopub)
103 stdout, stderr = assemble_output(iopub)
116 nt.assert_equal(stdout, '')
104 nt.assert_equal(stdout, '')
117 nt.assert_equal(stderr, '')
105 nt.assert_equal(stderr, '')
118
106
119 _check_mp_mode(kc, expected=False)
107 _check_mp_mode(kc, expected=False)
120 _check_mp_mode(kc, expected=False, stream="stderr")
108 _check_mp_mode(kc, expected=False, stream="stderr")
121
109
122
110
123 @dec.knownfailureif(sys.platform == 'win32', "subprocess prints fail on Windows")
111 @dec.knownfailureif(sys.platform == 'win32', "subprocess prints fail on Windows")
124 def test_subprocess_error():
112 def test_subprocess_error():
125 """error in mp.Process doesn't crash"""
113 """error in mp.Process doesn't crash"""
126 with new_kernel() as kc:
114 with new_kernel() as kc:
127 iopub = kc.iopub_channel
115 iopub = kc.iopub_channel
128
116
129 code = '\n'.join([
117 code = '\n'.join([
130 "import multiprocessing as mp",
118 "import multiprocessing as mp",
131 "p = mp.Process(target=int, args=('hi',))",
119 "p = mp.Process(target=int, args=('hi',))",
132 "p.start()",
120 "p.start()",
133 "p.join()",
121 "p.join()",
134 ])
122 ])
135
123
136 msg_id, content = execute(kc=kc, code=code)
124 msg_id, content = execute(kc=kc, code=code)
137 stdout, stderr = assemble_output(iopub)
125 stdout, stderr = assemble_output(iopub)
138 nt.assert_equal(stdout, '')
126 nt.assert_equal(stdout, '')
139 nt.assert_true("ValueError" in stderr, stderr)
127 nt.assert_true("ValueError" in stderr, stderr)
140
128
141 _check_mp_mode(kc, expected=False)
129 _check_mp_mode(kc, expected=False)
142 _check_mp_mode(kc, expected=False, stream="stderr")
130 _check_mp_mode(kc, expected=False, stream="stderr")
143
131
144 # raw_input tests
132 # raw_input tests
145
133
146 def test_raw_input():
134 def test_raw_input():
147 """test [raw_]input"""
135 """test [raw_]input"""
148 with kernel() as kc:
136 with kernel() as kc:
149 iopub = kc.iopub_channel
137 iopub = kc.iopub_channel
150
138
151 input_f = "input" if py3compat.PY3 else "raw_input"
139 input_f = "input" if py3compat.PY3 else "raw_input"
152 theprompt = "prompt> "
140 theprompt = "prompt> "
153 code = 'print({input_f}("{theprompt}"))'.format(**locals())
141 code = 'print({input_f}("{theprompt}"))'.format(**locals())
154 msg_id = kc.execute(code, allow_stdin=True)
142 msg_id = kc.execute(code, allow_stdin=True)
155 msg = kc.get_stdin_msg(block=True, timeout=TIMEOUT)
143 msg = kc.get_stdin_msg(block=True, timeout=TIMEOUT)
156 nt.assert_equal(msg['header']['msg_type'], u'input_request')
144 nt.assert_equal(msg['header']['msg_type'], u'input_request')
157 content = msg['content']
145 content = msg['content']
158 nt.assert_equal(content['prompt'], theprompt)
146 nt.assert_equal(content['prompt'], theprompt)
159 text = "some text"
147 text = "some text"
160 kc.input(text)
148 kc.input(text)
161 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
149 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
162 nt.assert_equal(reply['content']['status'], 'ok')
150 nt.assert_equal(reply['content']['status'], 'ok')
163 stdout, stderr = assemble_output(iopub)
151 stdout, stderr = assemble_output(iopub)
164 nt.assert_equal(stdout, text + "\n")
152 nt.assert_equal(stdout, text + "\n")
165
153
166
154
167 @dec.skipif(py3compat.PY3)
155 @dec.skipif(py3compat.PY3)
168 def test_eval_input():
156 def test_eval_input():
169 """test input() on Python 2"""
157 """test input() on Python 2"""
170 with kernel() as kc:
158 with kernel() as kc:
171 iopub = kc.iopub_channel
159 iopub = kc.iopub_channel
172
160
173 input_f = "input" if py3compat.PY3 else "raw_input"
161 input_f = "input" if py3compat.PY3 else "raw_input"
174 theprompt = "prompt> "
162 theprompt = "prompt> "
175 code = 'print(input("{theprompt}"))'.format(**locals())
163 code = 'print(input("{theprompt}"))'.format(**locals())
176 msg_id = kc.execute(code, allow_stdin=True)
164 msg_id = kc.execute(code, allow_stdin=True)
177 msg = kc.get_stdin_msg(block=True, timeout=TIMEOUT)
165 msg = kc.get_stdin_msg(block=True, timeout=TIMEOUT)
178 nt.assert_equal(msg['header']['msg_type'], u'input_request')
166 nt.assert_equal(msg['header']['msg_type'], u'input_request')
179 content = msg['content']
167 content = msg['content']
180 nt.assert_equal(content['prompt'], theprompt)
168 nt.assert_equal(content['prompt'], theprompt)
181 kc.input("1+1")
169 kc.input("1+1")
182 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
170 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
183 nt.assert_equal(reply['content']['status'], 'ok')
171 nt.assert_equal(reply['content']['status'], 'ok')
184 stdout, stderr = assemble_output(iopub)
172 stdout, stderr = assemble_output(iopub)
185 nt.assert_equal(stdout, "2\n")
173 nt.assert_equal(stdout, "2\n")
186
174
187
175
188 def test_save_history():
176 def test_save_history():
189 # Saving history from the kernel with %hist -f was failing because of
177 # Saving history from the kernel with %hist -f was failing because of
190 # unicode problems on Python 2.
178 # unicode problems on Python 2.
191 with kernel() as kc, TemporaryDirectory() as td:
179 with kernel() as kc, TemporaryDirectory() as td:
192 file = os.path.join(td, 'hist.out')
180 file = os.path.join(td, 'hist.out')
193 execute(u'a=1', kc=kc)
181 execute(u'a=1', kc=kc)
194 wait_for_idle(kc)
182 wait_for_idle(kc)
195 execute(u'b=u"abcΓΎ"', kc=kc)
183 execute(u'b=u"abcΓΎ"', kc=kc)
196 wait_for_idle(kc)
184 wait_for_idle(kc)
197 _, reply = execute("%hist -f " + file, kc=kc)
185 _, reply = execute("%hist -f " + file, kc=kc)
198 nt.assert_equal(reply['status'], 'ok')
186 nt.assert_equal(reply['status'], 'ok')
199 with io.open(file, encoding='utf-8') as f:
187 with io.open(file, encoding='utf-8') as f:
200 content = f.read()
188 content = f.read()
201 nt.assert_in(u'a=1', content)
189 nt.assert_in(u'a=1', content)
202 nt.assert_in(u'b=u"abcΓΎ"', content)
190 nt.assert_in(u'b=u"abcΓΎ"', content)
203
191
204 def test_help_output():
192 def test_help_output():
205 """ipython kernel --help-all works"""
193 """ipython kernel --help-all works"""
206 tt.help_all_output_test('kernel')
194 tt.help_all_output_test('kernel')
207
195
208 def test_is_complete():
196 def test_is_complete():
209 with kernel() as kc:
197 with kernel() as kc:
210 # There are more test cases for this in core - here we just check
198 # There are more test cases for this in core - here we just check
211 # that the kernel exposes the interface correctly.
199 # that the kernel exposes the interface correctly.
212 kc.is_complete('2+2')
200 kc.is_complete('2+2')
213 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
201 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
214 assert reply['content']['status'] == 'complete'
202 assert reply['content']['status'] == 'complete'
215
203
216 # SyntaxError should mean it's complete
204 # SyntaxError should mean it's complete
217 kc.is_complete('raise = 2')
205 kc.is_complete('raise = 2')
218 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
206 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
219 assert reply['content']['status'] == 'invalid'
207 assert reply['content']['status'] == 'invalid'
220
208
221 kc.is_complete('a = [1,\n2,')
209 kc.is_complete('a = [1,\n2,')
222 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
210 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
223 assert reply['content']['status'] == 'incomplete'
211 assert reply['content']['status'] == 'incomplete'
224 assert reply['content']['indent'] == '' No newline at end of file
212 assert reply['content']['indent'] == ''
213
214 def test_complete():
215 with kernel() as kc:
216 execute(u'a = 1', kc=kc)
217 wait_for_idle(kc)
218 cell = 'import IPython\nb = a.'
219 kc.complete(cell)
220 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
221 c = reply['content']
222 nt.assert_equal(c['status'], 'ok')
223 nt.assert_equal(c['cursor_start'], cell.find('a.'))
224 nt.assert_equal(c['cursor_end'], cell.find('a.') + 2)
225 matches = c['matches']
226 nt.assert_greater(len(matches), 0)
227 for match in matches:
228 nt.assert_equal(match[:2], 'a.')
@@ -1,159 +1,161 b''
1 """utilities for testing IPython kernels"""
1 """utilities for testing IPython kernels"""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6 import atexit
6 import atexit
7
7
8 from contextlib import contextmanager
8 from contextlib import contextmanager
9 from subprocess import PIPE, STDOUT
9 from subprocess import PIPE, STDOUT
10 try:
10 try:
11 from queue import Empty # Py 3
11 from queue import Empty # Py 3
12 except ImportError:
12 except ImportError:
13 from Queue import Empty # Py 2
13 from Queue import Empty # Py 2
14
14
15 import nose
15 import nose
16 import nose.tools as nt
16 import nose.tools as nt
17
17
18 from IPython.kernel import manager
18 from IPython.kernel import manager
19
19
20 #-------------------------------------------------------------------------------
20 #-------------------------------------------------------------------------------
21 # Globals
21 # Globals
22 #-------------------------------------------------------------------------------
22 #-------------------------------------------------------------------------------
23
23
24 STARTUP_TIMEOUT = 60
24 STARTUP_TIMEOUT = 60
25 TIMEOUT = 15
25 TIMEOUT = 15
26
26
27 KM = None
27 KM = None
28 KC = None
28 KC = None
29
29
30 #-------------------------------------------------------------------------------
30 #-------------------------------------------------------------------------------
31 # code
31 # code
32 #-------------------------------------------------------------------------------
32 #-------------------------------------------------------------------------------
33 def start_new_kernel(**kwargs):
33 def start_new_kernel(**kwargs):
34 """start a new kernel, and return its Manager and Client
34 """start a new kernel, and return its Manager and Client
35
35
36 Integrates with our output capturing for tests.
36 Integrates with our output capturing for tests.
37 """
37 """
38 kwargs.update(dict(stdout=nose.iptest_stdstreams_fileno(), stderr=STDOUT))
38 kwargs.update(dict(stdout=nose.iptest_stdstreams_fileno(), stderr=STDOUT))
39 return manager.start_new_kernel(startup_timeout=STARTUP_TIMEOUT, **kwargs)
39 return manager.start_new_kernel(startup_timeout=STARTUP_TIMEOUT, **kwargs)
40
40
41 def flush_channels(kc=None):
41 def flush_channels(kc=None):
42 """flush any messages waiting on the queue"""
42 """flush any messages waiting on the queue"""
43 from .test_message_spec import validate_message
43 from .test_message_spec import validate_message
44
44
45 if kc is None:
45 if kc is None:
46 kc = KC
46 kc = KC
47 for channel in (kc.shell_channel, kc.iopub_channel):
47 for channel in (kc.shell_channel, kc.iopub_channel):
48 while True:
48 while True:
49 try:
49 try:
50 msg = channel.get_msg(block=True, timeout=0.1)
50 msg = channel.get_msg(block=True, timeout=0.1)
51 except Empty:
51 except Empty:
52 break
52 break
53 else:
53 else:
54 validate_message(msg)
54 validate_message(msg)
55
55
56
56
57 def execute(code='', kc=None, **kwargs):
57 def execute(code='', kc=None, **kwargs):
58 """wrapper for doing common steps for validating an execution request"""
58 """wrapper for doing common steps for validating an execution request"""
59 from .test_message_spec import validate_message
59 from .test_message_spec import validate_message
60 if kc is None:
60 if kc is None:
61 kc = KC
61 kc = KC
62 msg_id = kc.execute(code=code, **kwargs)
62 msg_id = kc.execute(code=code, **kwargs)
63 reply = kc.get_shell_msg(timeout=TIMEOUT)
63 reply = kc.get_shell_msg(timeout=TIMEOUT)
64 validate_message(reply, 'execute_reply', msg_id)
64 validate_message(reply, 'execute_reply', msg_id)
65 busy = kc.get_iopub_msg(timeout=TIMEOUT)
65 busy = kc.get_iopub_msg(timeout=TIMEOUT)
66 validate_message(busy, 'status', msg_id)
66 validate_message(busy, 'status', msg_id)
67 nt.assert_equal(busy['content']['execution_state'], 'busy')
67 nt.assert_equal(busy['content']['execution_state'], 'busy')
68
68
69 if not kwargs.get('silent'):
69 if not kwargs.get('silent'):
70 execute_input = kc.get_iopub_msg(timeout=TIMEOUT)
70 execute_input = kc.get_iopub_msg(timeout=TIMEOUT)
71 validate_message(execute_input, 'execute_input', msg_id)
71 validate_message(execute_input, 'execute_input', msg_id)
72 nt.assert_equal(execute_input['content']['code'], code)
72 nt.assert_equal(execute_input['content']['code'], code)
73
73
74 return msg_id, reply['content']
74 return msg_id, reply['content']
75
75
76 def start_global_kernel():
76 def start_global_kernel():
77 """start the global kernel (if it isn't running) and return its client"""
77 """start the global kernel (if it isn't running) and return its client"""
78 global KM, KC
78 global KM, KC
79 if KM is None:
79 if KM is None:
80 KM, KC = start_new_kernel()
80 KM, KC = start_new_kernel()
81 atexit.register(stop_global_kernel)
81 atexit.register(stop_global_kernel)
82 else:
83 flush_channels(KC)
82 return KC
84 return KC
83
85
84 @contextmanager
86 @contextmanager
85 def kernel():
87 def kernel():
86 """Context manager for the global kernel instance
88 """Context manager for the global kernel instance
87
89
88 Should be used for most kernel tests
90 Should be used for most kernel tests
89
91
90 Returns
92 Returns
91 -------
93 -------
92 kernel_client: connected KernelClient instance
94 kernel_client: connected KernelClient instance
93 """
95 """
94 yield start_global_kernel()
96 yield start_global_kernel()
95
97
96 def uses_kernel(test_f):
98 def uses_kernel(test_f):
97 """Decorator for tests that use the global kernel"""
99 """Decorator for tests that use the global kernel"""
98 def wrapped_test():
100 def wrapped_test():
99 with kernel() as kc:
101 with kernel() as kc:
100 test_f(kc)
102 test_f(kc)
101 wrapped_test.__doc__ = test_f.__doc__
103 wrapped_test.__doc__ = test_f.__doc__
102 wrapped_test.__name__ = test_f.__name__
104 wrapped_test.__name__ = test_f.__name__
103 return wrapped_test
105 return wrapped_test
104
106
105 def stop_global_kernel():
107 def stop_global_kernel():
106 """Stop the global shared kernel instance, if it exists"""
108 """Stop the global shared kernel instance, if it exists"""
107 global KM, KC
109 global KM, KC
108 KC.stop_channels()
110 KC.stop_channels()
109 KC = None
111 KC = None
110 if KM is None:
112 if KM is None:
111 return
113 return
112 KM.shutdown_kernel(now=True)
114 KM.shutdown_kernel(now=True)
113 KM = None
115 KM = None
114
116
115 def new_kernel(argv=None):
117 def new_kernel(argv=None):
116 """Context manager for a new kernel in a subprocess
118 """Context manager for a new kernel in a subprocess
117
119
118 Should only be used for tests where the kernel must not be re-used.
120 Should only be used for tests where the kernel must not be re-used.
119
121
120 Returns
122 Returns
121 -------
123 -------
122 kernel_client: connected KernelClient instance
124 kernel_client: connected KernelClient instance
123 """
125 """
124 kwargs = dict(stdout=nose.iptest_stdstreams_fileno(), stderr=STDOUT,
126 kwargs = dict(stdout=nose.iptest_stdstreams_fileno(), stderr=STDOUT,
125 startup_timeout=STARTUP_TIMEOUT)
127 startup_timeout=STARTUP_TIMEOUT)
126 if argv is not None:
128 if argv is not None:
127 kwargs['extra_arguments'] = argv
129 kwargs['extra_arguments'] = argv
128 return manager.run_kernel(**kwargs)
130 return manager.run_kernel(**kwargs)
129
131
130 def assemble_output(iopub):
132 def assemble_output(iopub):
131 """assemble stdout/err from an execution"""
133 """assemble stdout/err from an execution"""
132 stdout = ''
134 stdout = ''
133 stderr = ''
135 stderr = ''
134 while True:
136 while True:
135 msg = iopub.get_msg(block=True, timeout=1)
137 msg = iopub.get_msg(block=True, timeout=1)
136 msg_type = msg['msg_type']
138 msg_type = msg['msg_type']
137 content = msg['content']
139 content = msg['content']
138 if msg_type == 'status' and content['execution_state'] == 'idle':
140 if msg_type == 'status' and content['execution_state'] == 'idle':
139 # idle message signals end of output
141 # idle message signals end of output
140 break
142 break
141 elif msg['msg_type'] == 'stream':
143 elif msg['msg_type'] == 'stream':
142 if content['name'] == 'stdout':
144 if content['name'] == 'stdout':
143 stdout += content['text']
145 stdout += content['text']
144 elif content['name'] == 'stderr':
146 elif content['name'] == 'stderr':
145 stderr += content['text']
147 stderr += content['text']
146 else:
148 else:
147 raise KeyError("bad stream: %r" % content['name'])
149 raise KeyError("bad stream: %r" % content['name'])
148 else:
150 else:
149 # other output, ignored
151 # other output, ignored
150 pass
152 pass
151 return stdout, stderr
153 return stdout, stderr
152
154
153 def wait_for_idle(kc):
155 def wait_for_idle(kc):
154 while True:
156 while True:
155 msg = kc.iopub_channel.get_msg(block=True, timeout=1)
157 msg = kc.iopub_channel.get_msg(block=True, timeout=1)
156 msg_type = msg['msg_type']
158 msg_type = msg['msg_type']
157 content = msg['content']
159 content = msg['content']
158 if msg_type == 'status' and content['execution_state'] == 'idle':
160 if msg_type == 'status' and content['execution_state'] == 'idle':
159 break
161 break
@@ -1,312 +1,320 b''
1 """The IPython kernel implementation"""
1 """The IPython kernel implementation"""
2
2
3 import getpass
3 import getpass
4 import sys
4 import sys
5 import traceback
5 import traceback
6
6
7 from IPython.core import release
7 from IPython.core import release
8 from IPython.utils.py3compat import builtin_mod, PY3
8 from IPython.utils.py3compat import builtin_mod, PY3
9 from IPython.utils.tokenutil import token_at_cursor
9 from IPython.utils.tokenutil import token_at_cursor, line_at_cursor
10 from IPython.utils.traitlets import Instance, Type, Any
10 from IPython.utils.traitlets import Instance, Type, Any
11 from IPython.utils.decorators import undoc
11 from IPython.utils.decorators import undoc
12
12
13 from ..comm import CommManager
13 from ..comm import CommManager
14 from .kernelbase import Kernel as KernelBase
14 from .kernelbase import Kernel as KernelBase
15 from .serialize import serialize_object, unpack_apply_message
15 from .serialize import serialize_object, unpack_apply_message
16 from .zmqshell import ZMQInteractiveShell
16 from .zmqshell import ZMQInteractiveShell
17
17
18 class IPythonKernel(KernelBase):
18 class IPythonKernel(KernelBase):
19 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
19 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
20 shell_class = Type(ZMQInteractiveShell)
20 shell_class = Type(ZMQInteractiveShell)
21
21
22 user_module = Any()
22 user_module = Any()
23 def _user_module_changed(self, name, old, new):
23 def _user_module_changed(self, name, old, new):
24 if self.shell is not None:
24 if self.shell is not None:
25 self.shell.user_module = new
25 self.shell.user_module = new
26
26
27 user_ns = Instance(dict, args=None, allow_none=True)
27 user_ns = Instance(dict, args=None, allow_none=True)
28 def _user_ns_changed(self, name, old, new):
28 def _user_ns_changed(self, name, old, new):
29 if self.shell is not None:
29 if self.shell is not None:
30 self.shell.user_ns = new
30 self.shell.user_ns = new
31 self.shell.init_user_ns()
31 self.shell.init_user_ns()
32
32
33 # A reference to the Python builtin 'raw_input' function.
33 # A reference to the Python builtin 'raw_input' function.
34 # (i.e., __builtin__.raw_input for Python 2.7, builtins.input for Python 3)
34 # (i.e., __builtin__.raw_input for Python 2.7, builtins.input for Python 3)
35 _sys_raw_input = Any()
35 _sys_raw_input = Any()
36 _sys_eval_input = Any()
36 _sys_eval_input = Any()
37
37
38 def __init__(self, **kwargs):
38 def __init__(self, **kwargs):
39 super(IPythonKernel, self).__init__(**kwargs)
39 super(IPythonKernel, self).__init__(**kwargs)
40
40
41 # Initialize the InteractiveShell subclass
41 # Initialize the InteractiveShell subclass
42 self.shell = self.shell_class.instance(parent=self,
42 self.shell = self.shell_class.instance(parent=self,
43 profile_dir = self.profile_dir,
43 profile_dir = self.profile_dir,
44 user_module = self.user_module,
44 user_module = self.user_module,
45 user_ns = self.user_ns,
45 user_ns = self.user_ns,
46 kernel = self,
46 kernel = self,
47 )
47 )
48 self.shell.displayhook.session = self.session
48 self.shell.displayhook.session = self.session
49 self.shell.displayhook.pub_socket = self.iopub_socket
49 self.shell.displayhook.pub_socket = self.iopub_socket
50 self.shell.displayhook.topic = self._topic('execute_result')
50 self.shell.displayhook.topic = self._topic('execute_result')
51 self.shell.display_pub.session = self.session
51 self.shell.display_pub.session = self.session
52 self.shell.display_pub.pub_socket = self.iopub_socket
52 self.shell.display_pub.pub_socket = self.iopub_socket
53 self.shell.data_pub.session = self.session
53 self.shell.data_pub.session = self.session
54 self.shell.data_pub.pub_socket = self.iopub_socket
54 self.shell.data_pub.pub_socket = self.iopub_socket
55
55
56 # TMP - hack while developing
56 # TMP - hack while developing
57 self.shell._reply_content = None
57 self.shell._reply_content = None
58
58
59 self.comm_manager = CommManager(shell=self.shell, parent=self,
59 self.comm_manager = CommManager(shell=self.shell, parent=self,
60 kernel=self)
60 kernel=self)
61 self.shell.configurables.append(self.comm_manager)
61 self.shell.configurables.append(self.comm_manager)
62 comm_msg_types = [ 'comm_open', 'comm_msg', 'comm_close' ]
62 comm_msg_types = [ 'comm_open', 'comm_msg', 'comm_close' ]
63 for msg_type in comm_msg_types:
63 for msg_type in comm_msg_types:
64 self.shell_handlers[msg_type] = getattr(self.comm_manager, msg_type)
64 self.shell_handlers[msg_type] = getattr(self.comm_manager, msg_type)
65
65
66 # Kernel info fields
66 # Kernel info fields
67 implementation = 'ipython'
67 implementation = 'ipython'
68 implementation_version = release.version
68 implementation_version = release.version
69 language = 'python'
69 language = 'python'
70 language_version = sys.version.split()[0]
70 language_version = sys.version.split()[0]
71 @property
71 @property
72 def banner(self):
72 def banner(self):
73 return self.shell.banner
73 return self.shell.banner
74
74
75 def start(self):
75 def start(self):
76 self.shell.exit_now = False
76 self.shell.exit_now = False
77 super(IPythonKernel, self).start()
77 super(IPythonKernel, self).start()
78
78
79 def set_parent(self, ident, parent):
79 def set_parent(self, ident, parent):
80 """Overridden from parent to tell the display hook and output streams
80 """Overridden from parent to tell the display hook and output streams
81 about the parent message.
81 about the parent message.
82 """
82 """
83 super(IPythonKernel, self).set_parent(ident, parent)
83 super(IPythonKernel, self).set_parent(ident, parent)
84 self.shell.set_parent(parent)
84 self.shell.set_parent(parent)
85
85
86 def _forward_input(self, allow_stdin=False):
86 def _forward_input(self, allow_stdin=False):
87 """Forward raw_input and getpass to the current frontend.
87 """Forward raw_input and getpass to the current frontend.
88
88
89 via input_request
89 via input_request
90 """
90 """
91 self._allow_stdin = allow_stdin
91 self._allow_stdin = allow_stdin
92
92
93 if PY3:
93 if PY3:
94 self._sys_raw_input = builtin_mod.input
94 self._sys_raw_input = builtin_mod.input
95 builtin_mod.input = self.raw_input
95 builtin_mod.input = self.raw_input
96 else:
96 else:
97 self._sys_raw_input = builtin_mod.raw_input
97 self._sys_raw_input = builtin_mod.raw_input
98 self._sys_eval_input = builtin_mod.input
98 self._sys_eval_input = builtin_mod.input
99 builtin_mod.raw_input = self.raw_input
99 builtin_mod.raw_input = self.raw_input
100 builtin_mod.input = lambda prompt='': eval(self.raw_input(prompt))
100 builtin_mod.input = lambda prompt='': eval(self.raw_input(prompt))
101 self._save_getpass = getpass.getpass
101 self._save_getpass = getpass.getpass
102 getpass.getpass = self.getpass
102 getpass.getpass = self.getpass
103
103
104 def _restore_input(self):
104 def _restore_input(self):
105 """Restore raw_input, getpass"""
105 """Restore raw_input, getpass"""
106 if PY3:
106 if PY3:
107 builtin_mod.input = self._sys_raw_input
107 builtin_mod.input = self._sys_raw_input
108 else:
108 else:
109 builtin_mod.raw_input = self._sys_raw_input
109 builtin_mod.raw_input = self._sys_raw_input
110 builtin_mod.input = self._sys_eval_input
110 builtin_mod.input = self._sys_eval_input
111
111
112 getpass.getpass = self._save_getpass
112 getpass.getpass = self._save_getpass
113
113
114 @property
114 @property
115 def execution_count(self):
115 def execution_count(self):
116 return self.shell.execution_count
116 return self.shell.execution_count
117
117
118 @execution_count.setter
118 @execution_count.setter
119 def execution_count(self, value):
119 def execution_count(self, value):
120 # Ignore the incrememnting done by KernelBase, in favour of our shell's
120 # Ignore the incrememnting done by KernelBase, in favour of our shell's
121 # execution counter.
121 # execution counter.
122 pass
122 pass
123
123
124 def do_execute(self, code, silent, store_history=True,
124 def do_execute(self, code, silent, store_history=True,
125 user_expressions=None, allow_stdin=False):
125 user_expressions=None, allow_stdin=False):
126 shell = self.shell # we'll need this a lot here
126 shell = self.shell # we'll need this a lot here
127
127
128 self._forward_input(allow_stdin)
128 self._forward_input(allow_stdin)
129
129
130 reply_content = {}
130 reply_content = {}
131 # FIXME: the shell calls the exception handler itself.
131 # FIXME: the shell calls the exception handler itself.
132 shell._reply_content = None
132 shell._reply_content = None
133 try:
133 try:
134 shell.run_cell(code, store_history=store_history, silent=silent)
134 shell.run_cell(code, store_history=store_history, silent=silent)
135 except:
135 except:
136 status = u'error'
136 status = u'error'
137 # FIXME: this code right now isn't being used yet by default,
137 # FIXME: this code right now isn't being used yet by default,
138 # because the run_cell() call above directly fires off exception
138 # because the run_cell() call above directly fires off exception
139 # reporting. This code, therefore, is only active in the scenario
139 # reporting. This code, therefore, is only active in the scenario
140 # where runlines itself has an unhandled exception. We need to
140 # where runlines itself has an unhandled exception. We need to
141 # uniformize this, for all exception construction to come from a
141 # uniformize this, for all exception construction to come from a
142 # single location in the codbase.
142 # single location in the codbase.
143 etype, evalue, tb = sys.exc_info()
143 etype, evalue, tb = sys.exc_info()
144 tb_list = traceback.format_exception(etype, evalue, tb)
144 tb_list = traceback.format_exception(etype, evalue, tb)
145 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
145 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
146 else:
146 else:
147 status = u'ok'
147 status = u'ok'
148 finally:
148 finally:
149 self._restore_input()
149 self._restore_input()
150
150
151 reply_content[u'status'] = status
151 reply_content[u'status'] = status
152
152
153 # Return the execution counter so clients can display prompts
153 # Return the execution counter so clients can display prompts
154 reply_content['execution_count'] = shell.execution_count - 1
154 reply_content['execution_count'] = shell.execution_count - 1
155
155
156 # FIXME - fish exception info out of shell, possibly left there by
156 # FIXME - fish exception info out of shell, possibly left there by
157 # runlines. We'll need to clean up this logic later.
157 # runlines. We'll need to clean up this logic later.
158 if shell._reply_content is not None:
158 if shell._reply_content is not None:
159 reply_content.update(shell._reply_content)
159 reply_content.update(shell._reply_content)
160 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
160 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
161 reply_content['engine_info'] = e_info
161 reply_content['engine_info'] = e_info
162 # reset after use
162 # reset after use
163 shell._reply_content = None
163 shell._reply_content = None
164
164
165 if 'traceback' in reply_content:
165 if 'traceback' in reply_content:
166 self.log.info("Exception in execute request:\n%s", '\n'.join(reply_content['traceback']))
166 self.log.info("Exception in execute request:\n%s", '\n'.join(reply_content['traceback']))
167
167
168
168
169 # At this point, we can tell whether the main code execution succeeded
169 # At this point, we can tell whether the main code execution succeeded
170 # or not. If it did, we proceed to evaluate user_expressions
170 # or not. If it did, we proceed to evaluate user_expressions
171 if reply_content['status'] == 'ok':
171 if reply_content['status'] == 'ok':
172 reply_content[u'user_expressions'] = \
172 reply_content[u'user_expressions'] = \
173 shell.user_expressions(user_expressions or {})
173 shell.user_expressions(user_expressions or {})
174 else:
174 else:
175 # If there was an error, don't even try to compute expressions
175 # If there was an error, don't even try to compute expressions
176 reply_content[u'user_expressions'] = {}
176 reply_content[u'user_expressions'] = {}
177
177
178 # Payloads should be retrieved regardless of outcome, so we can both
178 # Payloads should be retrieved regardless of outcome, so we can both
179 # recover partial output (that could have been generated early in a
179 # recover partial output (that could have been generated early in a
180 # block, before an error) and clear the payload system always.
180 # block, before an error) and clear the payload system always.
181 reply_content[u'payload'] = shell.payload_manager.read_payload()
181 reply_content[u'payload'] = shell.payload_manager.read_payload()
182 # Be agressive about clearing the payload because we don't want
182 # Be agressive about clearing the payload because we don't want
183 # it to sit in memory until the next execute_request comes in.
183 # it to sit in memory until the next execute_request comes in.
184 shell.payload_manager.clear_payload()
184 shell.payload_manager.clear_payload()
185
185
186 return reply_content
186 return reply_content
187
187
188 def do_complete(self, code, cursor_pos):
188 def do_complete(self, code, cursor_pos):
189 txt, matches = self.shell.complete('', code, cursor_pos)
189 # FIXME: IPython completers currently assume single line,
190 # but completion messages give multi-line context
191 # For now, extract line from cell, based on cursor_pos:
192 if cursor_pos is None:
193 cursor_pos = len(code)
194 line, offset = line_at_cursor(code, cursor_pos)
195 line_cursor = cursor_pos - offset
196
197 txt, matches = self.shell.complete('', line, line_cursor)
190 return {'matches' : matches,
198 return {'matches' : matches,
191 'cursor_end' : cursor_pos,
199 'cursor_end' : cursor_pos,
192 'cursor_start' : cursor_pos - len(txt),
200 'cursor_start' : cursor_pos - len(txt),
193 'metadata' : {},
201 'metadata' : {},
194 'status' : 'ok'}
202 'status' : 'ok'}
195
203
196 def do_inspect(self, code, cursor_pos, detail_level=0):
204 def do_inspect(self, code, cursor_pos, detail_level=0):
197 name = token_at_cursor(code, cursor_pos)
205 name = token_at_cursor(code, cursor_pos)
198 info = self.shell.object_inspect(name)
206 info = self.shell.object_inspect(name)
199
207
200 reply_content = {'status' : 'ok'}
208 reply_content = {'status' : 'ok'}
201 reply_content['data'] = data = {}
209 reply_content['data'] = data = {}
202 reply_content['metadata'] = {}
210 reply_content['metadata'] = {}
203 reply_content['found'] = info['found']
211 reply_content['found'] = info['found']
204 if info['found']:
212 if info['found']:
205 info_text = self.shell.object_inspect_text(
213 info_text = self.shell.object_inspect_text(
206 name,
214 name,
207 detail_level=detail_level,
215 detail_level=detail_level,
208 )
216 )
209 data['text/plain'] = info_text
217 data['text/plain'] = info_text
210
218
211 return reply_content
219 return reply_content
212
220
213 def do_history(self, hist_access_type, output, raw, session=None, start=None,
221 def do_history(self, hist_access_type, output, raw, session=None, start=None,
214 stop=None, n=None, pattern=None, unique=False):
222 stop=None, n=None, pattern=None, unique=False):
215 if hist_access_type == 'tail':
223 if hist_access_type == 'tail':
216 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
224 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
217 include_latest=True)
225 include_latest=True)
218
226
219 elif hist_access_type == 'range':
227 elif hist_access_type == 'range':
220 hist = self.shell.history_manager.get_range(session, start, stop,
228 hist = self.shell.history_manager.get_range(session, start, stop,
221 raw=raw, output=output)
229 raw=raw, output=output)
222
230
223 elif hist_access_type == 'search':
231 elif hist_access_type == 'search':
224 hist = self.shell.history_manager.search(
232 hist = self.shell.history_manager.search(
225 pattern, raw=raw, output=output, n=n, unique=unique)
233 pattern, raw=raw, output=output, n=n, unique=unique)
226 else:
234 else:
227 hist = []
235 hist = []
228
236
229 return {'history' : list(hist)}
237 return {'history' : list(hist)}
230
238
231 def do_shutdown(self, restart):
239 def do_shutdown(self, restart):
232 self.shell.exit_now = True
240 self.shell.exit_now = True
233 return dict(status='ok', restart=restart)
241 return dict(status='ok', restart=restart)
234
242
235 def do_is_complete(self, code):
243 def do_is_complete(self, code):
236 status, indent_spaces = self.shell.input_transformer_manager.check_complete(code)
244 status, indent_spaces = self.shell.input_transformer_manager.check_complete(code)
237 r = {'status': status}
245 r = {'status': status}
238 if status == 'incomplete':
246 if status == 'incomplete':
239 r['indent'] = ' ' * indent_spaces
247 r['indent'] = ' ' * indent_spaces
240 return r
248 return r
241
249
242 def do_apply(self, content, bufs, msg_id, reply_metadata):
250 def do_apply(self, content, bufs, msg_id, reply_metadata):
243 shell = self.shell
251 shell = self.shell
244 try:
252 try:
245 working = shell.user_ns
253 working = shell.user_ns
246
254
247 prefix = "_"+str(msg_id).replace("-","")+"_"
255 prefix = "_"+str(msg_id).replace("-","")+"_"
248
256
249 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
257 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
250
258
251 fname = getattr(f, '__name__', 'f')
259 fname = getattr(f, '__name__', 'f')
252
260
253 fname = prefix+"f"
261 fname = prefix+"f"
254 argname = prefix+"args"
262 argname = prefix+"args"
255 kwargname = prefix+"kwargs"
263 kwargname = prefix+"kwargs"
256 resultname = prefix+"result"
264 resultname = prefix+"result"
257
265
258 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
266 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
259 # print ns
267 # print ns
260 working.update(ns)
268 working.update(ns)
261 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
269 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
262 try:
270 try:
263 exec(code, shell.user_global_ns, shell.user_ns)
271 exec(code, shell.user_global_ns, shell.user_ns)
264 result = working.get(resultname)
272 result = working.get(resultname)
265 finally:
273 finally:
266 for key in ns:
274 for key in ns:
267 working.pop(key)
275 working.pop(key)
268
276
269 result_buf = serialize_object(result,
277 result_buf = serialize_object(result,
270 buffer_threshold=self.session.buffer_threshold,
278 buffer_threshold=self.session.buffer_threshold,
271 item_threshold=self.session.item_threshold,
279 item_threshold=self.session.item_threshold,
272 )
280 )
273
281
274 except:
282 except:
275 # invoke IPython traceback formatting
283 # invoke IPython traceback formatting
276 shell.showtraceback()
284 shell.showtraceback()
277 # FIXME - fish exception info out of shell, possibly left there by
285 # FIXME - fish exception info out of shell, possibly left there by
278 # run_code. We'll need to clean up this logic later.
286 # run_code. We'll need to clean up this logic later.
279 reply_content = {}
287 reply_content = {}
280 if shell._reply_content is not None:
288 if shell._reply_content is not None:
281 reply_content.update(shell._reply_content)
289 reply_content.update(shell._reply_content)
282 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
290 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
283 reply_content['engine_info'] = e_info
291 reply_content['engine_info'] = e_info
284 # reset after use
292 # reset after use
285 shell._reply_content = None
293 shell._reply_content = None
286
294
287 self.send_response(self.iopub_socket, u'error', reply_content,
295 self.send_response(self.iopub_socket, u'error', reply_content,
288 ident=self._topic('error'))
296 ident=self._topic('error'))
289 self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback']))
297 self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback']))
290 result_buf = []
298 result_buf = []
291
299
292 if reply_content['ename'] == 'UnmetDependency':
300 if reply_content['ename'] == 'UnmetDependency':
293 reply_metadata['dependencies_met'] = False
301 reply_metadata['dependencies_met'] = False
294 else:
302 else:
295 reply_content = {'status' : 'ok'}
303 reply_content = {'status' : 'ok'}
296
304
297 return reply_content, result_buf
305 return reply_content, result_buf
298
306
299 def do_clear(self):
307 def do_clear(self):
300 self.shell.reset(False)
308 self.shell.reset(False)
301 return dict(status='ok')
309 return dict(status='ok')
302
310
303
311
304 # This exists only for backwards compatibility - use IPythonKernel instead
312 # This exists only for backwards compatibility - use IPythonKernel instead
305
313
306 @undoc
314 @undoc
307 class Kernel(IPythonKernel):
315 class Kernel(IPythonKernel):
308 def __init__(self, *args, **kwargs):
316 def __init__(self, *args, **kwargs):
309 import warnings
317 import warnings
310 warnings.warn('Kernel is a deprecated alias of IPython.kernel.zmq.ipkernel.IPythonKernel',
318 warnings.warn('Kernel is a deprecated alias of IPython.kernel.zmq.ipkernel.IPythonKernel',
311 DeprecationWarning)
319 DeprecationWarning)
312 super(Kernel, self).__init__(*args, **kwargs) No newline at end of file
320 super(Kernel, self).__init__(*args, **kwargs)
@@ -1,80 +1,108 b''
1 """Token-related utilities"""
1 """Token-related utilities"""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6 from __future__ import absolute_import, print_function
6 from __future__ import absolute_import, print_function
7
7
8 from collections import namedtuple
8 from collections import namedtuple
9 from io import StringIO
9 from io import StringIO
10 from keyword import iskeyword
10 from keyword import iskeyword
11
11
12 from . import tokenize2
12 from . import tokenize2
13 from .py3compat import cast_unicode_py2
13 from .py3compat import cast_unicode_py2
14
14
15 Token = namedtuple('Token', ['token', 'text', 'start', 'end', 'line'])
15 Token = namedtuple('Token', ['token', 'text', 'start', 'end', 'line'])
16
16
17 def generate_tokens(readline):
17 def generate_tokens(readline):
18 """wrap generate_tokens to catch EOF errors"""
18 """wrap generate_tokens to catch EOF errors"""
19 try:
19 try:
20 for token in tokenize2.generate_tokens(readline):
20 for token in tokenize2.generate_tokens(readline):
21 yield token
21 yield token
22 except tokenize2.TokenError:
22 except tokenize2.TokenError:
23 # catch EOF error
23 # catch EOF error
24 return
24 return
25
25
26 def line_at_cursor(cell, cursor_pos=0):
27 """Return the line in a cell at a given cursor position
28
29 Used for calling line-based APIs that don't support multi-line input, yet.
30
31 Parameters
32 ----------
33
34 cell: text
35 multiline block of text
36 cursor_pos: integer
37 the cursor position
38
39 Returns
40 -------
41
42 (line, offset): (text, integer)
43 The line with the current cursor, and the character offset of the start of the line.
44 """
45 offset = 0
46 lines = cell.splitlines(True)
47 for line in lines:
48 next_offset = offset + len(line)
49 if next_offset >= cursor_pos:
50 break
51 offset = next_offset
52 return (line, offset)
53
26 def token_at_cursor(cell, cursor_pos=0):
54 def token_at_cursor(cell, cursor_pos=0):
27 """Get the token at a given cursor
55 """Get the token at a given cursor
28
56
29 Used for introspection.
57 Used for introspection.
30
58
31 Parameters
59 Parameters
32 ----------
60 ----------
33
61
34 cell : unicode
62 cell : unicode
35 A block of Python code
63 A block of Python code
36 cursor_pos : int
64 cursor_pos : int
37 The location of the cursor in the block where the token should be found
65 The location of the cursor in the block where the token should be found
38 """
66 """
39 cell = cast_unicode_py2(cell)
67 cell = cast_unicode_py2(cell)
40 names = []
68 names = []
41 tokens = []
69 tokens = []
42 offset = 0
70 offset = 0
43 for tup in generate_tokens(StringIO(cell).readline):
71 for tup in generate_tokens(StringIO(cell).readline):
44
72
45 tok = Token(*tup)
73 tok = Token(*tup)
46
74
47 # token, text, start, end, line = tup
75 # token, text, start, end, line = tup
48 start_col = tok.start[1]
76 start_col = tok.start[1]
49 end_col = tok.end[1]
77 end_col = tok.end[1]
50 # allow '|foo' to find 'foo' at the beginning of a line
78 # allow '|foo' to find 'foo' at the beginning of a line
51 boundary = cursor_pos + 1 if start_col == 0 else cursor_pos
79 boundary = cursor_pos + 1 if start_col == 0 else cursor_pos
52 if offset + start_col >= boundary:
80 if offset + start_col >= boundary:
53 # current token starts after the cursor,
81 # current token starts after the cursor,
54 # don't consume it
82 # don't consume it
55 break
83 break
56
84
57 if tok.token == tokenize2.NAME and not iskeyword(tok.text):
85 if tok.token == tokenize2.NAME and not iskeyword(tok.text):
58 if names and tokens and tokens[-1].token == tokenize2.OP and tokens[-1].text == '.':
86 if names and tokens and tokens[-1].token == tokenize2.OP and tokens[-1].text == '.':
59 names[-1] = "%s.%s" % (names[-1], tok.text)
87 names[-1] = "%s.%s" % (names[-1], tok.text)
60 else:
88 else:
61 names.append(tok.text)
89 names.append(tok.text)
62 elif tok.token == tokenize2.OP:
90 elif tok.token == tokenize2.OP:
63 if tok.text == '=' and names:
91 if tok.text == '=' and names:
64 # don't inspect the lhs of an assignment
92 # don't inspect the lhs of an assignment
65 names.pop(-1)
93 names.pop(-1)
66
94
67 if offset + end_col > cursor_pos:
95 if offset + end_col > cursor_pos:
68 # we found the cursor, stop reading
96 # we found the cursor, stop reading
69 break
97 break
70
98
71 tokens.append(tok)
99 tokens.append(tok)
72 if tok.token == tokenize2.NEWLINE:
100 if tok.token == tokenize2.NEWLINE:
73 offset += len(tok.line)
101 offset += len(tok.line)
74
102
75 if names:
103 if names:
76 return names[-1]
104 return names[-1]
77 else:
105 else:
78 return ''
106 return ''
79
107
80
108
General Comments 0
You need to be logged in to leave comments. Login now