##// END OF EJS Templates
Use stream capturer for kernel tests
Thomas Kluyver -
Show More
@@ -1,170 +1,173 b''
1 """utilities for testing IPython kernels"""
1 """utilities for testing IPython kernels"""
2
2
3 #-------------------------------------------------------------------------------
3 #-------------------------------------------------------------------------------
4 # Copyright (C) 2013 The IPython Development Team
4 # Copyright (C) 2013 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, distributed as part of this software.
7 # the file COPYING, distributed as part of this software.
8 #-------------------------------------------------------------------------------
8 #-------------------------------------------------------------------------------
9
9
10 #-------------------------------------------------------------------------------
10 #-------------------------------------------------------------------------------
11 # Imports
11 # Imports
12 #-------------------------------------------------------------------------------
12 #-------------------------------------------------------------------------------
13
13
14 import atexit
14 import atexit
15
15
16 from contextlib import contextmanager
16 from contextlib import contextmanager
17 from subprocess import PIPE
17 from subprocess import PIPE, STDOUT
18 from Queue import Empty
18 from Queue import Empty
19
19
20 import nose
20 import nose.tools as nt
21 import nose.tools as nt
21
22
22 from IPython.kernel import KernelManager
23 from IPython.kernel import KernelManager
23
24
24 #-------------------------------------------------------------------------------
25 #-------------------------------------------------------------------------------
25 # Globals
26 # Globals
26 #-------------------------------------------------------------------------------
27 #-------------------------------------------------------------------------------
27
28
28 STARTUP_TIMEOUT = 60
29 STARTUP_TIMEOUT = 60
29 TIMEOUT = 15
30 TIMEOUT = 15
30
31
31 KM = None
32 KM = None
32 KC = None
33 KC = None
33
34
34 #-------------------------------------------------------------------------------
35 #-------------------------------------------------------------------------------
35 # code
36 # code
36 #-------------------------------------------------------------------------------
37 #-------------------------------------------------------------------------------
37
38
38
39
39 def start_new_kernel(argv=None):
40 def start_new_kernel(argv=None):
40 """start a new kernel, and return its Manager and Client"""
41 """start a new kernel, and return its Manager and Client"""
41 km = KernelManager()
42 km = KernelManager()
42 kwargs = dict(stdout=PIPE, stderr=PIPE)
43 kwargs = dict(stdout=PIPE, stderr=STDOUT)
43 if argv:
44 if argv:
44 kwargs['extra_arguments'] = argv
45 kwargs['extra_arguments'] = argv
45 km.start_kernel(**kwargs)
46 km.start_kernel(**kwargs)
47 nose.ipy_stream_capturer.add_stream(km.kernel.stdout.fileno())
48 nose.ipy_stream_capturer.ensure_started()
46 kc = km.client()
49 kc = km.client()
47 kc.start_channels()
50 kc.start_channels()
48
51
49 msg_id = kc.kernel_info()
52 msg_id = kc.kernel_info()
50 kc.get_shell_msg(block=True, timeout=STARTUP_TIMEOUT)
53 kc.get_shell_msg(block=True, timeout=STARTUP_TIMEOUT)
51 flush_channels(kc)
54 flush_channels(kc)
52 return km, kc
55 return km, kc
53
56
54 def flush_channels(kc=None):
57 def flush_channels(kc=None):
55 """flush any messages waiting on the queue"""
58 """flush any messages waiting on the queue"""
56 from .test_message_spec import validate_message
59 from .test_message_spec import validate_message
57
60
58 if kc is None:
61 if kc is None:
59 kc = KC
62 kc = KC
60 for channel in (kc.shell_channel, kc.iopub_channel):
63 for channel in (kc.shell_channel, kc.iopub_channel):
61 while True:
64 while True:
62 try:
65 try:
63 msg = channel.get_msg(block=True, timeout=0.1)
66 msg = channel.get_msg(block=True, timeout=0.1)
64 except Empty:
67 except Empty:
65 break
68 break
66 else:
69 else:
67 validate_message(msg)
70 validate_message(msg)
68
71
69
72
70 def execute(code='', kc=None, **kwargs):
73 def execute(code='', kc=None, **kwargs):
71 """wrapper for doing common steps for validating an execution request"""
74 """wrapper for doing common steps for validating an execution request"""
72 from .test_message_spec import validate_message
75 from .test_message_spec import validate_message
73 if kc is None:
76 if kc is None:
74 kc = KC
77 kc = KC
75 msg_id = kc.execute(code=code, **kwargs)
78 msg_id = kc.execute(code=code, **kwargs)
76 reply = kc.get_shell_msg(timeout=TIMEOUT)
79 reply = kc.get_shell_msg(timeout=TIMEOUT)
77 validate_message(reply, 'execute_reply', msg_id)
80 validate_message(reply, 'execute_reply', msg_id)
78 busy = kc.get_iopub_msg(timeout=TIMEOUT)
81 busy = kc.get_iopub_msg(timeout=TIMEOUT)
79 validate_message(busy, 'status', msg_id)
82 validate_message(busy, 'status', msg_id)
80 nt.assert_equal(busy['content']['execution_state'], 'busy')
83 nt.assert_equal(busy['content']['execution_state'], 'busy')
81
84
82 if not kwargs.get('silent'):
85 if not kwargs.get('silent'):
83 pyin = kc.get_iopub_msg(timeout=TIMEOUT)
86 pyin = kc.get_iopub_msg(timeout=TIMEOUT)
84 validate_message(pyin, 'pyin', msg_id)
87 validate_message(pyin, 'pyin', msg_id)
85 nt.assert_equal(pyin['content']['code'], code)
88 nt.assert_equal(pyin['content']['code'], code)
86
89
87 return msg_id, reply['content']
90 return msg_id, reply['content']
88
91
89 def start_global_kernel():
92 def start_global_kernel():
90 """start the global kernel (if it isn't running) and return its client"""
93 """start the global kernel (if it isn't running) and return its client"""
91 global KM, KC
94 global KM, KC
92 if KM is None:
95 if KM is None:
93 KM, KC = start_new_kernel()
96 KM, KC = start_new_kernel()
94 atexit.register(stop_global_kernel)
97 atexit.register(stop_global_kernel)
95 return KC
98 return KC
96
99
97 @contextmanager
100 @contextmanager
98 def kernel():
101 def kernel():
99 """Context manager for the global kernel instance
102 """Context manager for the global kernel instance
100
103
101 Should be used for most kernel tests
104 Should be used for most kernel tests
102
105
103 Returns
106 Returns
104 -------
107 -------
105 kernel_client: connected KernelClient instance
108 kernel_client: connected KernelClient instance
106 """
109 """
107 yield start_global_kernel()
110 yield start_global_kernel()
108
111
109 def uses_kernel(test_f):
112 def uses_kernel(test_f):
110 """Decorator for tests that use the global kernel"""
113 """Decorator for tests that use the global kernel"""
111 def wrapped_test():
114 def wrapped_test():
112 with kernel() as kc:
115 with kernel() as kc:
113 test_f(kc)
116 test_f(kc)
114 wrapped_test.__doc__ = test_f.__doc__
117 wrapped_test.__doc__ = test_f.__doc__
115 wrapped_test.__name__ = test_f.__name__
118 wrapped_test.__name__ = test_f.__name__
116 return wrapped_test
119 return wrapped_test
117
120
118 def stop_global_kernel():
121 def stop_global_kernel():
119 """Stop the global shared kernel instance, if it exists"""
122 """Stop the global shared kernel instance, if it exists"""
120 global KM, KC
123 global KM, KC
121 KC.stop_channels()
124 KC.stop_channels()
122 KC = None
125 KC = None
123 if KM is None:
126 if KM is None:
124 return
127 return
125 KM.shutdown_kernel(now=True)
128 KM.shutdown_kernel(now=True)
126 KM = None
129 KM = None
127
130
128 @contextmanager
131 @contextmanager
129 def new_kernel(argv=None):
132 def new_kernel(argv=None):
130 """Context manager for a new kernel in a subprocess
133 """Context manager for a new kernel in a subprocess
131
134
132 Should only be used for tests where the kernel must not be re-used.
135 Should only be used for tests where the kernel must not be re-used.
133
136
134 Returns
137 Returns
135 -------
138 -------
136 kernel_client: connected KernelClient instance
139 kernel_client: connected KernelClient instance
137 """
140 """
138 km, kc = start_new_kernel(argv)
141 km, kc = start_new_kernel(argv)
139 try:
142 try:
140 yield kc
143 yield kc
141 finally:
144 finally:
142 kc.stop_channels()
145 kc.stop_channels()
143 km.shutdown_kernel(now=True)
146 km.shutdown_kernel(now=True)
144
147
145
148
146 def assemble_output(iopub):
149 def assemble_output(iopub):
147 """assemble stdout/err from an execution"""
150 """assemble stdout/err from an execution"""
148 stdout = ''
151 stdout = ''
149 stderr = ''
152 stderr = ''
150 while True:
153 while True:
151 msg = iopub.get_msg(block=True, timeout=1)
154 msg = iopub.get_msg(block=True, timeout=1)
152 msg_type = msg['msg_type']
155 msg_type = msg['msg_type']
153 content = msg['content']
156 content = msg['content']
154 if msg_type == 'status' and content['execution_state'] == 'idle':
157 if msg_type == 'status' and content['execution_state'] == 'idle':
155 # idle message signals end of output
158 # idle message signals end of output
156 break
159 break
157 elif msg['msg_type'] == 'stream':
160 elif msg['msg_type'] == 'stream':
158 if content['name'] == 'stdout':
161 if content['name'] == 'stdout':
159 stdout += content['data']
162 stdout += content['data']
160 elif content['name'] == 'stderr':
163 elif content['name'] == 'stderr':
161 stderr += content['data']
164 stderr += content['data']
162 else:
165 else:
163 raise KeyError("bad stream: %r" % content['name'])
166 raise KeyError("bad stream: %r" % content['name'])
164 else:
167 else:
165 # other output, ignored
168 # other output, ignored
166 pass
169 pass
167 return stdout, stderr
170 return stdout, stderr
168
171
169
172
170
173
General Comments 0
You need to be logged in to leave comments. Login now