##// END OF EJS Templates
handle pyout messages in test_message_spec...
MinRK -
Show More
@@ -1,437 +1,452 b''
1 1 """Test suite for our zeromq-based messaging specification.
2 2 """
3 3 #-----------------------------------------------------------------------------
4 4 # Copyright (C) 2010-2011 The IPython Development Team
5 5 #
6 6 # Distributed under the terms of the BSD License. The full license is in
7 7 # the file COPYING.txt, distributed as part of this software.
8 8 #-----------------------------------------------------------------------------
9 9
10 10 import re
11 11 import sys
12 12 import time
13 13 from subprocess import PIPE
14 14 from Queue import Empty
15 15
16 16 import nose.tools as nt
17 17
18 18 from ..blockingkernelmanager import BlockingKernelManager
19 19
20 20
21 21 from IPython.testing import decorators as dec
22 22 from IPython.utils import io
23 23 from IPython.utils.traitlets import (
24 24 HasTraits, TraitError, Bool, Unicode, Dict, Integer, List, Enum,
25 25 )
26 26
27 27 #-----------------------------------------------------------------------------
28 28 # Global setup and utilities
29 29 #-----------------------------------------------------------------------------
30 30
31 31 def setup():
32 32 global KM
33 33 KM = BlockingKernelManager()
34 34
35 35 KM.start_kernel(stdout=PIPE, stderr=PIPE)
36 36 KM.start_channels()
37 37
38 # wait for kernel to be ready
39 KM.shell_channel.execute("pass")
40 KM.shell_channel.get_msg(block=True, timeout=5)
41 flush_channels()
42
38 43
39 44 def teardown():
40 45 KM.stop_channels()
41 46 KM.shutdown_kernel()
42 47
43 48
44 49 def flush_channels():
45 50 """flush any messages waiting on the queue"""
46 51 for channel in (KM.shell_channel, KM.sub_channel):
47 52 while True:
48 53 try:
49 54 msg = channel.get_msg(block=True, timeout=0.1)
50 55 except Empty:
51 56 break
52 57 else:
53 58 list(validate_message(msg))
54 59
55 60
56 61 def execute(code='', **kwargs):
57 62 """wrapper for doing common steps for validating an execution request"""
58 63 shell = KM.shell_channel
59 64 sub = KM.sub_channel
60 65
61 66 msg_id = shell.execute(code=code, **kwargs)
62 67 reply = shell.get_msg(timeout=2)
63 68 list(validate_message(reply, 'execute_reply', msg_id))
64 69 busy = sub.get_msg(timeout=2)
65 70 list(validate_message(busy, 'status', msg_id))
66 71 nt.assert_equals(busy['content']['execution_state'], 'busy')
67 72
68 73 if not kwargs.get('silent'):
69 74 pyin = sub.get_msg(timeout=2)
70 75 list(validate_message(pyin, 'pyin', msg_id))
71 76 nt.assert_equals(pyin['content']['code'], code)
72 77
73 78 return msg_id, reply['content']
74 79
75 80 #-----------------------------------------------------------------------------
76 81 # MSG Spec References
77 82 #-----------------------------------------------------------------------------
78 83
79 84
80 85 class Reference(HasTraits):
81 86
82 87 def check(self, d):
83 88 """validate a dict against our traits"""
84 89 for key in self.trait_names():
85 90 yield nt.assert_true(key in d, "Missing key: %r, should be found in %s" % (key, d))
86 91 # FIXME: always allow None, probably not a good idea
87 92 if d[key] is None:
88 93 continue
89 94 try:
90 95 setattr(self, key, d[key])
91 96 except TraitError as e:
92 97 yield nt.assert_true(False, str(e))
93 98
94 99
95 100 class RMessage(Reference):
96 101 msg_id = Unicode()
97 102 msg_type = Unicode()
98 103 header = Dict()
99 104 parent_header = Dict()
100 105 content = Dict()
101 106
102 107 class RHeader(Reference):
103 108 msg_id = Unicode()
104 109 msg_type = Unicode()
105 110 session = Unicode()
106 111 username = Unicode()
107 112
108 113 class RContent(Reference):
109 114 status = Enum((u'ok', u'error'))
110 115
111 116
112 117 class ExecuteReply(Reference):
113 118 execution_count = Integer()
114 119 status = Enum((u'ok', u'error'))
115 120
116 121 def check(self, d):
117 122 for tst in Reference.check(self, d):
118 123 yield tst
119 124 if d['status'] == 'ok':
120 125 for tst in ExecuteReplyOkay().check(d):
121 126 yield tst
122 127 elif d['status'] == 'error':
123 128 for tst in ExecuteReplyError().check(d):
124 129 yield tst
125 130
126 131
127 132 class ExecuteReplyOkay(Reference):
128 133 payload = List(Dict)
129 134 user_variables = Dict()
130 135 user_expressions = Dict()
131 136
132 137
133 138 class ExecuteReplyError(Reference):
134 139 ename = Unicode()
135 140 evalue = Unicode()
136 141 traceback = List(Unicode)
137 142
138 143
139 144 class OInfoReply(Reference):
140 145 name = Unicode()
141 146 found = Bool()
142 147 ismagic = Bool()
143 148 isalias = Bool()
144 149 namespace = Enum((u'builtin', u'magics', u'alias', u'Interactive'))
145 150 type_name = Unicode()
146 151 string_form = Unicode()
147 152 base_class = Unicode()
148 153 length = Integer()
149 154 file = Unicode()
150 155 definition = Unicode()
151 156 argspec = Dict()
152 157 init_definition = Unicode()
153 158 docstring = Unicode()
154 159 init_docstring = Unicode()
155 160 class_docstring = Unicode()
156 161 call_def = Unicode()
157 162 call_docstring = Unicode()
158 163 source = Unicode()
159 164
160 165 def check(self, d):
161 166 for tst in Reference.check(self, d):
162 167 yield tst
163 168 if d['argspec'] is not None:
164 169 for tst in ArgSpec().check(d['argspec']):
165 170 yield tst
166 171
167 172
168 173 class ArgSpec(Reference):
169 174 args = List(Unicode)
170 175 varargs = Unicode()
171 176 varkw = Unicode()
172 177 defaults = List()
173 178
174 179
175 180 class Status(Reference):
176 181 execution_state = Enum((u'busy', u'idle'))
177 182
178 183
179 184 class CompleteReply(Reference):
180 185 matches = List(Unicode)
181 186
182 187
183 188 # IOPub messages
184 189
185 190 class PyIn(Reference):
186 191 code = Unicode()
187 192 execution_count = Integer()
188 193
189 194
190 195 PyErr = ExecuteReplyError
191 196
192 197
193 198 class Stream(Reference):
194 199 name = Enum((u'stdout', u'stderr'))
195 200 data = Unicode()
196 201
197 202
198 203 mime_pat = re.compile(r'\w+/\w+')
199 204
200 205 class DisplayData(Reference):
201 206 source = Unicode()
202 207 metadata = Dict()
203 208 data = Dict()
204 209 def _data_changed(self, name, old, new):
205 210 for k,v in new.iteritems():
206 211 nt.assert_true(mime_pat.match(k))
207 212 nt.assert_true(isinstance(v, basestring), "expected string data, got %r" % v)
208 213
209 214
215 class PyOut(Reference):
216 execution_count = Integer()
217 data = Dict()
218 def _data_changed(self, name, old, new):
219 for k,v in new.iteritems():
220 nt.assert_true(mime_pat.match(k))
221 nt.assert_true(isinstance(v, basestring), "expected string data, got %r" % v)
222
223
210 224 references = {
211 225 'execute_reply' : ExecuteReply(),
212 226 'object_info_reply' : OInfoReply(),
213 227 'status' : Status(),
214 228 'complete_reply' : CompleteReply(),
215 229 'pyin' : PyIn(),
230 'pyout' : PyOut(),
216 231 'pyerr' : PyErr(),
217 232 'stream' : Stream(),
218 233 'display_data' : DisplayData(),
219 234 }
220 235
221 236
222 237 def validate_message(msg, msg_type=None, parent=None):
223 238 """validate a message
224 239
225 240 This is a generator, and must be iterated through to actually
226 241 trigger each test.
227 242
228 243 If msg_type and/or parent are given, the msg_type and/or parent msg_id
229 244 are compared with the given values.
230 245 """
231 246 RMessage().check(msg)
232 247 if msg_type:
233 248 yield nt.assert_equals(msg['msg_type'], msg_type)
234 249 if parent:
235 250 yield nt.assert_equal(msg['parent_header']['msg_id'], parent)
236 251 content = msg['content']
237 252 ref = references[msg['msg_type']]
238 253 for tst in ref.check(content):
239 254 yield tst
240 255
241 256
242 257 #-----------------------------------------------------------------------------
243 258 # Tests
244 259 #-----------------------------------------------------------------------------
245 260
246 261 # Shell channel
247 262
248 263 @dec.parametric
249 264 def test_execute():
250 265 flush_channels()
251 266
252 267 shell = KM.shell_channel
253 268 msg_id = shell.execute(code='x=1')
254 269 reply = shell.get_msg(timeout=2)
255 270 for tst in validate_message(reply, 'execute_reply', msg_id):
256 271 yield tst
257 272
258 273
259 274 @dec.parametric
260 275 def test_execute_silent():
261 276 flush_channels()
262 277 msg_id, reply = execute(code='x=1', silent=True)
263 278
264 279 # flush status=idle
265 280 status = KM.sub_channel.get_msg(timeout=2)
266 281 for tst in validate_message(status, 'status', msg_id):
267 282 yield tst
268 283 nt.assert_equals(status['content']['execution_state'], 'idle')
269 284
270 285 yield nt.assert_raises(Empty, KM.sub_channel.get_msg, timeout=0.1)
271 286 count = reply['execution_count']
272 287
273 288 msg_id, reply = execute(code='x=2', silent=True)
274 289
275 290 # flush status=idle
276 291 status = KM.sub_channel.get_msg(timeout=2)
277 292 for tst in validate_message(status, 'status', msg_id):
278 293 yield tst
279 294 yield nt.assert_equals(status['content']['execution_state'], 'idle')
280 295
281 296 yield nt.assert_raises(Empty, KM.sub_channel.get_msg, timeout=0.1)
282 297 count_2 = reply['execution_count']
283 298 yield nt.assert_equals(count_2, count)
284 299
285 300
286 301 @dec.parametric
287 302 def test_execute_error():
288 303 flush_channels()
289 304
290 305 msg_id, reply = execute(code='1/0')
291 306 yield nt.assert_equals(reply['status'], 'error')
292 307 yield nt.assert_equals(reply['ename'], 'ZeroDivisionError')
293 308
294 309 pyerr = KM.sub_channel.get_msg(timeout=2)
295 310 for tst in validate_message(pyerr, 'pyerr', msg_id):
296 311 yield tst
297 312
298 313
299 314 def test_execute_inc():
300 315 """execute request should increment execution_count"""
301 316 flush_channels()
302 317
303 318 msg_id, reply = execute(code='x=1')
304 319 count = reply['execution_count']
305 320
306 321 flush_channels()
307 322
308 323 msg_id, reply = execute(code='x=2')
309 324 count_2 = reply['execution_count']
310 325 nt.assert_equals(count_2, count+1)
311 326
312 327
313 328 def test_user_variables():
314 329 flush_channels()
315 330
316 331 msg_id, reply = execute(code='x=1', user_variables=['x'])
317 332 user_variables = reply['user_variables']
318 333 nt.assert_equals(user_variables, {u'x' : u'1'})
319 334
320 335
321 336 def test_user_expressions():
322 337 flush_channels()
323 338
324 339 msg_id, reply = execute(code='x=1', user_expressions=dict(foo='x+1'))
325 340 user_expressions = reply['user_expressions']
326 341 nt.assert_equals(user_expressions, {u'foo' : u'2'})
327 342
328 343
329 344 @dec.parametric
330 345 def test_oinfo():
331 346 flush_channels()
332 347
333 348 shell = KM.shell_channel
334 349
335 350 msg_id = shell.object_info('a')
336 351 reply = shell.get_msg(timeout=2)
337 352 for tst in validate_message(reply, 'object_info_reply', msg_id):
338 353 yield tst
339 354
340 355
341 356 @dec.parametric
342 357 def test_oinfo_found():
343 358 flush_channels()
344 359
345 360 shell = KM.shell_channel
346 361
347 362 msg_id, reply = execute(code='a=5')
348 363
349 364 msg_id = shell.object_info('a')
350 365 reply = shell.get_msg(timeout=2)
351 366 for tst in validate_message(reply, 'object_info_reply', msg_id):
352 367 yield tst
353 368 content = reply['content']
354 369 yield nt.assert_true(content['found'])
355 370 argspec = content['argspec']
356 371 yield nt.assert_true(argspec is None, "didn't expect argspec dict, got %r" % argspec)
357 372
358 373
359 374 @dec.parametric
360 375 def test_oinfo_detail():
361 376 flush_channels()
362 377
363 378 shell = KM.shell_channel
364 379
365 380 msg_id, reply = execute(code='ip=get_ipython()')
366 381
367 382 msg_id = shell.object_info('ip.object_inspect', detail_level=2)
368 383 reply = shell.get_msg(timeout=2)
369 384 for tst in validate_message(reply, 'object_info_reply', msg_id):
370 385 yield tst
371 386 content = reply['content']
372 387 yield nt.assert_true(content['found'])
373 388 argspec = content['argspec']
374 389 yield nt.assert_true(isinstance(argspec, dict), "expected non-empty argspec dict, got %r" % argspec)
375 390 yield nt.assert_equals(argspec['defaults'], [0])
376 391
377 392
378 393 @dec.parametric
379 394 def test_oinfo_not_found():
380 395 flush_channels()
381 396
382 397 shell = KM.shell_channel
383 398
384 399 msg_id = shell.object_info('dne')
385 400 reply = shell.get_msg(timeout=2)
386 401 for tst in validate_message(reply, 'object_info_reply', msg_id):
387 402 yield tst
388 403 content = reply['content']
389 404 yield nt.assert_false(content['found'])
390 405
391 406
392 407 @dec.parametric
393 408 def test_complete():
394 409 flush_channels()
395 410
396 411 shell = KM.shell_channel
397 412
398 413 msg_id, reply = execute(code="alpha = albert = 5")
399 414
400 415 msg_id = shell.complete('al', 'al', 2)
401 416 reply = shell.get_msg(timeout=2)
402 417 for tst in validate_message(reply, 'complete_reply', msg_id):
403 418 yield tst
404 419 matches = reply['content']['matches']
405 420 for name in ('alpha', 'albert'):
406 421 yield nt.assert_true(name in matches, "Missing match: %r" % name)
407 422
408 423
409 424 # IOPub channel
410 425
411 426
412 427 @dec.parametric
413 428 def test_stream():
414 429 flush_channels()
415 430
416 431 msg_id, reply = execute("print('hi')")
417 432
418 433 stdout = KM.sub_channel.get_msg(timeout=2)
419 434 for tst in validate_message(stdout, 'stream', msg_id):
420 435 yield tst
421 436 content = stdout['content']
422 437 yield nt.assert_equals(content['name'], u'stdout')
423 438 yield nt.assert_equals(content['data'], u'hi\n')
424 439
425 440
426 441 @dec.parametric
427 442 def test_display_data():
428 443 flush_channels()
429 444
430 445 msg_id, reply = execute("from IPython.core.display import display; display(1)")
431 446
432 447 display = KM.sub_channel.get_msg(timeout=2)
433 448 for tst in validate_message(display, 'display_data', parent=msg_id):
434 449 yield tst
435 450 data = display['content']['data']
436 451 yield nt.assert_equals(data['text/plain'], u'1')
437 452
General Comments 0
You need to be logged in to leave comments. Login now