##// END OF EJS Templates
Merge pull request #7553 from minrk/kernel-test-intermittent...
Matthias Bussonnier -
r20140:2507fe22 merge
parent child Browse files
Show More
@@ -1,490 +1,496 b''
1 1 """Test suite for our zeromq-based message specification."""
2 2
3 3 # Copyright (c) IPython Development Team.
4 4 # Distributed under the terms of the Modified BSD License.
5 5
6 6 import re
7 7 import sys
8 8 from distutils.version import LooseVersion as V
9 9 try:
10 10 from queue import Empty # Py 3
11 11 except ImportError:
12 12 from Queue import Empty # Py 2
13 13
14 14 import nose.tools as nt
15 15
16 16 from IPython.utils.traitlets import (
17 17 HasTraits, TraitError, Bool, Unicode, Dict, Integer, List, Enum,
18 18 )
19 19 from IPython.utils.py3compat import string_types, iteritems
20 20
21 21 from .utils import TIMEOUT, start_global_kernel, flush_channels, execute
22 22
23 23 #-----------------------------------------------------------------------------
24 24 # Globals
25 25 #-----------------------------------------------------------------------------
26 26 KC = None
27 27
28 28 def setup():
29 29 global KC
30 30 KC = start_global_kernel()
31 31
32 32 #-----------------------------------------------------------------------------
33 33 # Message Spec References
34 34 #-----------------------------------------------------------------------------
35 35
36 36 class Reference(HasTraits):
37 37
38 38 """
39 39 Base class for message spec specification testing.
40 40
41 41 This class is the core of the message specification test. The
42 42 idea is that child classes implement trait attributes for each
43 43 message keys, so that message keys can be tested against these
44 44 traits using :meth:`check` method.
45 45
46 46 """
47 47
48 48 def check(self, d):
49 49 """validate a dict against our traits"""
50 50 for key in self.trait_names():
51 51 nt.assert_in(key, d)
52 52 # FIXME: always allow None, probably not a good idea
53 53 if d[key] is None:
54 54 continue
55 55 try:
56 56 setattr(self, key, d[key])
57 57 except TraitError as e:
58 58 assert False, str(e)
59 59
60 60
61 61 class Version(Unicode):
62 62 def __init__(self, *args, **kwargs):
63 63 self.min = kwargs.pop('min', None)
64 64 self.max = kwargs.pop('max', None)
65 65 kwargs['default_value'] = self.min
66 66 super(Version, self).__init__(*args, **kwargs)
67 67
68 68 def validate(self, obj, value):
69 69 if self.min and V(value) < V(self.min):
70 70 raise TraitError("bad version: %s < %s" % (value, self.min))
71 71 if self.max and (V(value) > V(self.max)):
72 72 raise TraitError("bad version: %s > %s" % (value, self.max))
73 73
74 74
75 75 class RMessage(Reference):
76 76 msg_id = Unicode()
77 77 msg_type = Unicode()
78 78 header = Dict()
79 79 parent_header = Dict()
80 80 content = Dict()
81 81
82 82 def check(self, d):
83 83 super(RMessage, self).check(d)
84 84 RHeader().check(self.header)
85 85 if self.parent_header:
86 86 RHeader().check(self.parent_header)
87 87
88 88 class RHeader(Reference):
89 89 msg_id = Unicode()
90 90 msg_type = Unicode()
91 91 session = Unicode()
92 92 username = Unicode()
93 93 version = Version(min='5.0')
94 94
95 95 mime_pat = re.compile(r'^[\w\-\+\.]+/[\w\-\+\.]+$')
96 96
97 97 class MimeBundle(Reference):
98 98 metadata = Dict()
99 99 data = Dict()
100 100 def _data_changed(self, name, old, new):
101 101 for k,v in iteritems(new):
102 102 assert mime_pat.match(k)
103 103 nt.assert_is_instance(v, string_types)
104 104
105 105 # shell replies
106 106
107 107 class ExecuteReply(Reference):
108 108 execution_count = Integer()
109 109 status = Enum((u'ok', u'error'))
110 110
111 111 def check(self, d):
112 112 Reference.check(self, d)
113 113 if d['status'] == 'ok':
114 114 ExecuteReplyOkay().check(d)
115 115 elif d['status'] == 'error':
116 116 ExecuteReplyError().check(d)
117 117
118 118
119 119 class ExecuteReplyOkay(Reference):
120 120 payload = List(Dict)
121 121 user_expressions = Dict()
122 122
123 123
124 124 class ExecuteReplyError(Reference):
125 125 ename = Unicode()
126 126 evalue = Unicode()
127 127 traceback = List(Unicode)
128 128
129 129
130 130 class InspectReply(MimeBundle):
131 131 found = Bool()
132 132
133 133
134 134 class ArgSpec(Reference):
135 135 args = List(Unicode)
136 136 varargs = Unicode()
137 137 varkw = Unicode()
138 138 defaults = List()
139 139
140 140
141 141 class Status(Reference):
142 142 execution_state = Enum((u'busy', u'idle', u'starting'))
143 143
144 144
145 145 class CompleteReply(Reference):
146 146 matches = List(Unicode)
147 147 cursor_start = Integer()
148 148 cursor_end = Integer()
149 149 status = Unicode()
150 150
151 151 class LanguageInfo(Reference):
152 152 name = Unicode('python')
153 153 version = Unicode(sys.version.split()[0])
154 154
155 155 class KernelInfoReply(Reference):
156 156 protocol_version = Version(min='5.0')
157 157 implementation = Unicode('ipython')
158 158 implementation_version = Version(min='2.1')
159 159 language_info = Dict()
160 160 banner = Unicode()
161 161
162 162 def check(self, d):
163 163 Reference.check(self, d)
164 164 LanguageInfo().check(d['language_info'])
165 165
166 166
167 167 class IsCompleteReply(Reference):
168 168 status = Enum((u'complete', u'incomplete', u'invalid', u'unknown'))
169 169
170 170 def check(self, d):
171 171 Reference.check(self, d)
172 172 if d['status'] == 'incomplete':
173 173 IsCompleteReplyIncomplete().check(d)
174 174
175 175 class IsCompleteReplyIncomplete(Reference):
176 176 indent = Unicode()
177 177
178 178
179 179 # IOPub messages
180 180
181 181 class ExecuteInput(Reference):
182 182 code = Unicode()
183 183 execution_count = Integer()
184 184
185 185
186 186 Error = ExecuteReplyError
187 187
188 188
189 189 class Stream(Reference):
190 190 name = Enum((u'stdout', u'stderr'))
191 191 text = Unicode()
192 192
193 193
194 194 class DisplayData(MimeBundle):
195 195 pass
196 196
197 197
198 198 class ExecuteResult(MimeBundle):
199 199 execution_count = Integer()
200 200
201 201 class HistoryReply(Reference):
202 202 history = List(List())
203 203
204 204
205 205 references = {
206 206 'execute_reply' : ExecuteReply(),
207 207 'inspect_reply' : InspectReply(),
208 208 'status' : Status(),
209 209 'complete_reply' : CompleteReply(),
210 210 'kernel_info_reply': KernelInfoReply(),
211 211 'is_complete_reply': IsCompleteReply(),
212 212 'execute_input' : ExecuteInput(),
213 213 'execute_result' : ExecuteResult(),
214 214 'history_reply' : HistoryReply(),
215 215 'error' : Error(),
216 216 'stream' : Stream(),
217 217 'display_data' : DisplayData(),
218 218 'header' : RHeader(),
219 219 }
220 220 """
221 221 Specifications of `content` part of the reply messages.
222 222 """
223 223
224 224
225 225 def validate_message(msg, msg_type=None, parent=None):
226 226 """validate a message
227 227
228 228 This is a generator, and must be iterated through to actually
229 229 trigger each test.
230 230
231 231 If msg_type and/or parent are given, the msg_type and/or parent msg_id
232 232 are compared with the given values.
233 233 """
234 234 RMessage().check(msg)
235 235 if msg_type:
236 236 nt.assert_equal(msg['msg_type'], msg_type)
237 237 if parent:
238 238 nt.assert_equal(msg['parent_header']['msg_id'], parent)
239 239 content = msg['content']
240 240 ref = references[msg['msg_type']]
241 241 ref.check(content)
242 242
243 243
244 244 #-----------------------------------------------------------------------------
245 245 # Tests
246 246 #-----------------------------------------------------------------------------
247 247
248 248 # Shell channel
249 249
250 250 def test_execute():
251 251 flush_channels()
252 252
253 253 msg_id = KC.execute(code='x=1')
254 254 reply = KC.get_shell_msg(timeout=TIMEOUT)
255 255 validate_message(reply, 'execute_reply', msg_id)
256 256
257 257
258 258 def test_execute_silent():
259 259 flush_channels()
260 260 msg_id, reply = execute(code='x=1', silent=True)
261 261
262 262 # flush status=idle
263 263 status = KC.iopub_channel.get_msg(timeout=TIMEOUT)
264 264 validate_message(status, 'status', msg_id)
265 265 nt.assert_equal(status['content']['execution_state'], 'idle')
266 266
267 267 nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1)
268 268 count = reply['execution_count']
269 269
270 270 msg_id, reply = execute(code='x=2', silent=True)
271 271
272 272 # flush status=idle
273 273 status = KC.iopub_channel.get_msg(timeout=TIMEOUT)
274 274 validate_message(status, 'status', msg_id)
275 275 nt.assert_equal(status['content']['execution_state'], 'idle')
276 276
277 277 nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1)
278 278 count_2 = reply['execution_count']
279 279 nt.assert_equal(count_2, count)
280 280
281 281
282 282 def test_execute_error():
283 283 flush_channels()
284 284
285 285 msg_id, reply = execute(code='1/0')
286 286 nt.assert_equal(reply['status'], 'error')
287 287 nt.assert_equal(reply['ename'], 'ZeroDivisionError')
288 288
289 289 error = KC.iopub_channel.get_msg(timeout=TIMEOUT)
290 290 validate_message(error, 'error', msg_id)
291 291
292 292
293 293 def test_execute_inc():
294 294 """execute request should increment execution_count"""
295 295 flush_channels()
296 296
297 297 msg_id, reply = execute(code='x=1')
298 298 count = reply['execution_count']
299 299
300 300 flush_channels()
301 301
302 302 msg_id, reply = execute(code='x=2')
303 303 count_2 = reply['execution_count']
304 304 nt.assert_equal(count_2, count+1)
305 305
306 306 def test_execute_stop_on_error():
307 307 """execute request should not abort execution queue with stop_on_error False"""
308 308 flush_channels()
309
310 KC.execute(code='raise IOError')
309
310 fail = '\n'.join([
311 # sleep to ensure subsequent message is waiting in the queue to be aborted
312 'import time',
313 'time.sleep(0.5)',
314 'raise ValueError',
315 ])
316 KC.execute(code=fail)
311 317 msg_id = KC.execute(code='print("Hello")')
312 318 KC.get_shell_msg(timeout=TIMEOUT)
313 319 reply = KC.get_shell_msg(timeout=TIMEOUT)
314 320 nt.assert_equal(reply['content']['status'], 'aborted')
315 321
316 322 flush_channels()
317 323
318 KC.execute(code='raise IOError', stop_on_error=False)
324 KC.execute(code=fail, stop_on_error=False)
319 325 msg_id = KC.execute(code='print("Hello")')
320 326 KC.get_shell_msg(timeout=TIMEOUT)
321 327 reply = KC.get_shell_msg(timeout=TIMEOUT)
322 328 nt.assert_equal(reply['content']['status'], 'ok')
323 329
324 330
325 331 def test_user_expressions():
326 332 flush_channels()
327 333
328 334 msg_id, reply = execute(code='x=1', user_expressions=dict(foo='x+1'))
329 335 user_expressions = reply['user_expressions']
330 336 nt.assert_equal(user_expressions, {u'foo': {
331 337 u'status': u'ok',
332 338 u'data': {u'text/plain': u'2'},
333 339 u'metadata': {},
334 340 }})
335 341
336 342
337 343 def test_user_expressions_fail():
338 344 flush_channels()
339 345
340 346 msg_id, reply = execute(code='x=0', user_expressions=dict(foo='nosuchname'))
341 347 user_expressions = reply['user_expressions']
342 348 foo = user_expressions['foo']
343 349 nt.assert_equal(foo['status'], 'error')
344 350 nt.assert_equal(foo['ename'], 'NameError')
345 351
346 352
347 353 def test_oinfo():
348 354 flush_channels()
349 355
350 356 msg_id = KC.inspect('a')
351 357 reply = KC.get_shell_msg(timeout=TIMEOUT)
352 358 validate_message(reply, 'inspect_reply', msg_id)
353 359
354 360
355 361 def test_oinfo_found():
356 362 flush_channels()
357 363
358 364 msg_id, reply = execute(code='a=5')
359 365
360 366 msg_id = KC.inspect('a')
361 367 reply = KC.get_shell_msg(timeout=TIMEOUT)
362 368 validate_message(reply, 'inspect_reply', msg_id)
363 369 content = reply['content']
364 370 assert content['found']
365 371 text = content['data']['text/plain']
366 372 nt.assert_in('Type:', text)
367 373 nt.assert_in('Docstring:', text)
368 374
369 375
370 376 def test_oinfo_detail():
371 377 flush_channels()
372 378
373 379 msg_id, reply = execute(code='ip=get_ipython()')
374 380
375 381 msg_id = KC.inspect('ip.object_inspect', cursor_pos=10, detail_level=1)
376 382 reply = KC.get_shell_msg(timeout=TIMEOUT)
377 383 validate_message(reply, 'inspect_reply', msg_id)
378 384 content = reply['content']
379 385 assert content['found']
380 386 text = content['data']['text/plain']
381 387 nt.assert_in('Definition:', text)
382 388 nt.assert_in('Source:', text)
383 389
384 390
385 391 def test_oinfo_not_found():
386 392 flush_channels()
387 393
388 394 msg_id = KC.inspect('dne')
389 395 reply = KC.get_shell_msg(timeout=TIMEOUT)
390 396 validate_message(reply, 'inspect_reply', msg_id)
391 397 content = reply['content']
392 398 nt.assert_false(content['found'])
393 399
394 400
395 401 def test_complete():
396 402 flush_channels()
397 403
398 404 msg_id, reply = execute(code="alpha = albert = 5")
399 405
400 406 msg_id = KC.complete('al', 2)
401 407 reply = KC.get_shell_msg(timeout=TIMEOUT)
402 408 validate_message(reply, 'complete_reply', msg_id)
403 409 matches = reply['content']['matches']
404 410 for name in ('alpha', 'albert'):
405 411 nt.assert_in(name, matches)
406 412
407 413
408 414 def test_kernel_info_request():
409 415 flush_channels()
410 416
411 417 msg_id = KC.kernel_info()
412 418 reply = KC.get_shell_msg(timeout=TIMEOUT)
413 419 validate_message(reply, 'kernel_info_reply', msg_id)
414 420
415 421
416 422 def test_single_payload():
417 423 flush_channels()
418 424 msg_id, reply = execute(code="for i in range(3):\n"+
419 425 " x=range?\n")
420 426 payload = reply['payload']
421 427 next_input_pls = [pl for pl in payload if pl["source"] == "set_next_input"]
422 428 nt.assert_equal(len(next_input_pls), 1)
423 429
424 430 def test_is_complete():
425 431 flush_channels()
426 432
427 433 msg_id = KC.is_complete("a = 1")
428 434 reply = KC.get_shell_msg(timeout=TIMEOUT)
429 435 validate_message(reply, 'is_complete_reply', msg_id)
430 436
431 437 def test_history_range():
432 438 flush_channels()
433 439
434 440 msg_id_exec = KC.execute(code='x=1', store_history = True)
435 441 reply_exec = KC.get_shell_msg(timeout=TIMEOUT)
436 442
437 443 msg_id = KC.history(hist_access_type = 'range', raw = True, output = True, start = 1, stop = 2, session = 0)
438 444 reply = KC.get_shell_msg(timeout=TIMEOUT)
439 445 validate_message(reply, 'history_reply', msg_id)
440 446 content = reply['content']
441 447 nt.assert_equal(len(content['history']), 1)
442 448
443 449 def test_history_tail():
444 450 flush_channels()
445 451
446 452 msg_id_exec = KC.execute(code='x=1', store_history = True)
447 453 reply_exec = KC.get_shell_msg(timeout=TIMEOUT)
448 454
449 455 msg_id = KC.history(hist_access_type = 'tail', raw = True, output = True, n = 1, session = 0)
450 456 reply = KC.get_shell_msg(timeout=TIMEOUT)
451 457 validate_message(reply, 'history_reply', msg_id)
452 458 content = reply['content']
453 459 nt.assert_equal(len(content['history']), 1)
454 460
455 461 def test_history_search():
456 462 flush_channels()
457 463
458 464 msg_id_exec = KC.execute(code='x=1', store_history = True)
459 465 reply_exec = KC.get_shell_msg(timeout=TIMEOUT)
460 466
461 467 msg_id = KC.history(hist_access_type = 'search', raw = True, output = True, n = 1, pattern = '*', session = 0)
462 468 reply = KC.get_shell_msg(timeout=TIMEOUT)
463 469 validate_message(reply, 'history_reply', msg_id)
464 470 content = reply['content']
465 471 nt.assert_equal(len(content['history']), 1)
466 472
467 473 # IOPub channel
468 474
469 475
470 476 def test_stream():
471 477 flush_channels()
472 478
473 479 msg_id, reply = execute("print('hi')")
474 480
475 481 stdout = KC.iopub_channel.get_msg(timeout=TIMEOUT)
476 482 validate_message(stdout, 'stream', msg_id)
477 483 content = stdout['content']
478 484 nt.assert_equal(content['text'], u'hi\n')
479 485
480 486
481 487 def test_display_data():
482 488 flush_channels()
483 489
484 490 msg_id, reply = execute("from IPython.core.display import display; display(1)")
485 491
486 492 display = KC.iopub_channel.get_msg(timeout=TIMEOUT)
487 493 validate_message(display, 'display_data', parent=msg_id)
488 494 data = display['content']['data']
489 495 nt.assert_equal(data['text/plain'], u'1')
490 496
General Comments 0
You need to be logged in to leave comments. Login now