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