##// END OF EJS Templates
Fix faulty interact tests
Jonathan Frederic -
Show More
@@ -1,612 +1,611 b''
1 1 """Test interact and interactive."""
2 2
3 3 # Copyright (c) IPython Development Team.
4 4 # Distributed under the terms of the Modified BSD License.
5 5
6 6 from __future__ import print_function
7 7
8 8 from collections import OrderedDict
9 9
10 10 import nose.tools as nt
11 11 import IPython.testing.tools as tt
12 12
13 13 from IPython.kernel.comm import Comm
14 14 from IPython.html import widgets
15 15 from IPython.html.widgets import interact, interactive, Widget, interaction
16 16 from IPython.utils.py3compat import annotate
17 17
18 18 #-----------------------------------------------------------------------------
19 19 # Utility stuff
20 20 #-----------------------------------------------------------------------------
21 21
22 22 class DummyComm(Comm):
23 23 comm_id = 'a-b-c-d'
24 24
25 25 def open(self, *args, **kwargs):
26 26 pass
27 27
28 28 def send(self, *args, **kwargs):
29 29 pass
30 30
31 31 def close(self, *args, **kwargs):
32 32 pass
33 33
34 34 _widget_attrs = {}
35 35 displayed = []
36 36 undefined = object()
37 37
38 38 def setup():
39 39 _widget_attrs['_comm_default'] = getattr(Widget, '_comm_default', undefined)
40 40 Widget._comm_default = lambda self: DummyComm()
41 41 _widget_attrs['_ipython_display_'] = Widget._ipython_display_
42 42 def raise_not_implemented(*args, **kwargs):
43 43 raise NotImplementedError()
44 44 Widget._ipython_display_ = raise_not_implemented
45 45
46 46 def teardown():
47 47 for attr, value in _widget_attrs.items():
48 48 if value is undefined:
49 49 delattr(Widget, attr)
50 50 else:
51 51 setattr(Widget, attr, value)
52 52
53 53 def f(**kwargs):
54 54 pass
55 55
56 56 def clear_display():
57 57 global displayed
58 58 displayed = []
59 59
60 60 def record_display(*args):
61 61 displayed.extend(args)
62 62
63 63 #-----------------------------------------------------------------------------
64 64 # Actual tests
65 65 #-----------------------------------------------------------------------------
66 66
67 67 def check_widget(w, **d):
68 68 """Check a single widget against a dict"""
69 69 for attr, expected in d.items():
70 70 if attr == 'cls':
71 71 nt.assert_is(w.__class__, expected)
72 72 else:
73 73 value = getattr(w, attr)
74 74 nt.assert_equal(value, expected,
75 75 "%s.%s = %r != %r" % (w.__class__.__name__, attr, value, expected)
76 76 )
77 77
78 78 def check_widgets(container, **to_check):
79 79 """Check that widgets are created as expected"""
80 80 # build a widget dictionary, so it matches
81 81 widgets = {}
82 82 for w in container.children:
83 83 widgets[w.description] = w
84 84
85 85 for key, d in to_check.items():
86 86 nt.assert_in(key, widgets)
87 87 check_widget(widgets[key], **d)
88 88
89 89
90 90 def test_single_value_string():
91 91 a = u'hello'
92 92 c = interactive(f, a=a)
93 93 w = c.children[0]
94 94 check_widget(w,
95 95 cls=widgets.Text,
96 96 description='a',
97 97 value=a,
98 98 )
99 99
100 100 def test_single_value_bool():
101 101 for a in (True, False):
102 102 c = interactive(f, a=a)
103 103 w = c.children[0]
104 104 check_widget(w,
105 105 cls=widgets.Checkbox,
106 106 description='a',
107 107 value=a,
108 108 )
109 109
110 110 def test_single_value_dict():
111 111 for d in [
112 112 dict(a=5),
113 113 dict(a=5, b='b', c=dict),
114 114 ]:
115 115 c = interactive(f, d=d)
116 116 w = c.children[0]
117 117 check_widget(w,
118 118 cls=widgets.Dropdown,
119 119 description='d',
120 120 values=d,
121 121 value=next(iter(d.values())),
122 122 )
123 123
124 124 def test_single_value_float():
125 125 for a in (2.25, 1.0, -3.5):
126 126 c = interactive(f, a=a)
127 127 w = c.children[0]
128 128 check_widget(w,
129 129 cls=widgets.FloatSlider,
130 130 description='a',
131 131 value=a,
132 132 min= -a if a > 0 else 3*a,
133 133 max= 3*a if a > 0 else -a,
134 134 step=0.1,
135 135 readout=True,
136 136 )
137 137
138 138 def test_single_value_int():
139 139 for a in (1, 5, -3):
140 140 c = interactive(f, a=a)
141 141 nt.assert_equal(len(c.children), 1)
142 142 w = c.children[0]
143 143 check_widget(w,
144 144 cls=widgets.IntSlider,
145 145 description='a',
146 146 value=a,
147 147 min= -a if a > 0 else 3*a,
148 148 max= 3*a if a > 0 else -a,
149 149 step=1,
150 150 readout=True,
151 151 )
152 152
153 153 def test_list_tuple_2_int():
154 154 with nt.assert_raises(ValueError):
155 155 c = interactive(f, tup=(1,1))
156 156 with nt.assert_raises(ValueError):
157 157 c = interactive(f, tup=(1,-1))
158 158 for min, max in [ (0,1), (1,10), (1,2), (-5,5), (-20,-19) ]:
159 159 c = interactive(f, tup=(min, max), lis=[min, max])
160 160 nt.assert_equal(len(c.children), 2)
161 161 d = dict(
162 162 cls=widgets.IntSlider,
163 163 min=min,
164 164 max=max,
165 165 step=1,
166 166 readout=True,
167 167 )
168 168 check_widgets(c, tup=d, lis=d)
169 169
170 170 def test_list_tuple_3_int():
171 171 with nt.assert_raises(ValueError):
172 172 c = interactive(f, tup=(1,2,0))
173 173 with nt.assert_raises(ValueError):
174 174 c = interactive(f, tup=(1,2,-1))
175 175 for min, max, step in [ (0,2,1), (1,10,2), (1,100,2), (-5,5,4), (-100,-20,4) ]:
176 176 c = interactive(f, tup=(min, max, step), lis=[min, max, step])
177 177 nt.assert_equal(len(c.children), 2)
178 178 d = dict(
179 179 cls=widgets.IntSlider,
180 180 min=min,
181 181 max=max,
182 182 step=step,
183 183 readout=True,
184 184 )
185 185 check_widgets(c, tup=d, lis=d)
186 186
187 187 def test_list_tuple_2_float():
188 188 with nt.assert_raises(ValueError):
189 189 c = interactive(f, tup=(1.0,1.0))
190 190 with nt.assert_raises(ValueError):
191 191 c = interactive(f, tup=(0.5,-0.5))
192 192 for min, max in [ (0.5, 1.5), (1.1,10.2), (1,2.2), (-5.,5), (-20,-19.) ]:
193 193 c = interactive(f, tup=(min, max), lis=[min, max])
194 194 nt.assert_equal(len(c.children), 2)
195 195 d = dict(
196 196 cls=widgets.FloatSlider,
197 197 min=min,
198 198 max=max,
199 199 step=.1,
200 200 readout=True,
201 201 )
202 202 check_widgets(c, tup=d, lis=d)
203 203
204 204 def test_list_tuple_3_float():
205 205 with nt.assert_raises(ValueError):
206 206 c = interactive(f, tup=(1,2,0.0))
207 207 with nt.assert_raises(ValueError):
208 208 c = interactive(f, tup=(-1,-2,1.))
209 209 with nt.assert_raises(ValueError):
210 210 c = interactive(f, tup=(1,2.,-1.))
211 211 for min, max, step in [ (0.,2,1), (1,10.,2), (1,100,2.), (-5.,5.,4), (-100,-20.,4.) ]:
212 212 c = interactive(f, tup=(min, max, step), lis=[min, max, step])
213 213 nt.assert_equal(len(c.children), 2)
214 214 d = dict(
215 215 cls=widgets.FloatSlider,
216 216 min=min,
217 217 max=max,
218 218 step=step,
219 219 readout=True,
220 220 )
221 221 check_widgets(c, tup=d, lis=d)
222 222
223 223 def test_list_tuple_str():
224 224 values = ['hello', 'there', 'guy']
225 225 first = values[0]
226 dvalues = OrderedDict((v,v) for v in values)
227 226 c = interactive(f, tup=tuple(values), lis=list(values))
228 227 nt.assert_equal(len(c.children), 2)
229 228 d = dict(
230 229 cls=widgets.Dropdown,
231 230 value=first,
232 values=dvalues
231 values=values
233 232 )
234 233 check_widgets(c, tup=d, lis=d)
235 234
236 235 def test_list_tuple_invalid():
237 236 for bad in [
238 237 (),
239 238 (5, 'hi'),
240 239 ('hi', 5),
241 240 ({},),
242 241 (None,),
243 242 ]:
244 243 with nt.assert_raises(ValueError):
245 244 print(bad) # because there is no custom message in assert_raises
246 245 c = interactive(f, tup=bad)
247 246
248 247 def test_defaults():
249 248 @annotate(n=10)
250 249 def f(n, f=4.5, g=1):
251 250 pass
252 251
253 252 c = interactive(f)
254 253 check_widgets(c,
255 254 n=dict(
256 255 cls=widgets.IntSlider,
257 256 value=10,
258 257 ),
259 258 f=dict(
260 259 cls=widgets.FloatSlider,
261 260 value=4.5,
262 261 ),
263 262 g=dict(
264 263 cls=widgets.IntSlider,
265 264 value=1,
266 265 ),
267 266 )
268 267
269 268 def test_default_values():
270 269 @annotate(n=10, f=(0, 10.), g=5, h={'a': 1, 'b': 2}, j=['hi', 'there'])
271 270 def f(n, f=4.5, g=1, h=2, j='there'):
272 271 pass
273 272
274 273 c = interactive(f)
275 274 check_widgets(c,
276 275 n=dict(
277 276 cls=widgets.IntSlider,
278 277 value=10,
279 278 ),
280 279 f=dict(
281 280 cls=widgets.FloatSlider,
282 281 value=4.5,
283 282 ),
284 283 g=dict(
285 284 cls=widgets.IntSlider,
286 285 value=5,
287 286 ),
288 287 h=dict(
289 288 cls=widgets.Dropdown,
290 289 values={'a': 1, 'b': 2},
291 290 value=2
292 291 ),
293 292 j=dict(
294 293 cls=widgets.Dropdown,
295 values={'hi':'hi', 'there':'there'},
294 values=['hi', 'there'],
296 295 value='there'
297 296 ),
298 297 )
299 298
300 299 def test_default_out_of_bounds():
301 300 @annotate(f=(0, 10.), h={'a': 1}, j=['hi', 'there'])
302 301 def f(f='hi', h=5, j='other'):
303 302 pass
304 303
305 304 c = interactive(f)
306 305 check_widgets(c,
307 306 f=dict(
308 307 cls=widgets.FloatSlider,
309 308 value=5.,
310 309 ),
311 310 h=dict(
312 311 cls=widgets.Dropdown,
313 312 values={'a': 1},
314 313 value=1,
315 314 ),
316 315 j=dict(
317 316 cls=widgets.Dropdown,
318 values={'hi':'hi', 'there':'there'},
317 values=['hi', 'there'],
319 318 value='hi',
320 319 ),
321 320 )
322 321
323 322 def test_annotations():
324 323 @annotate(n=10, f=widgets.FloatText())
325 324 def f(n, f):
326 325 pass
327 326
328 327 c = interactive(f)
329 328 check_widgets(c,
330 329 n=dict(
331 330 cls=widgets.IntSlider,
332 331 value=10,
333 332 ),
334 333 f=dict(
335 334 cls=widgets.FloatText,
336 335 ),
337 336 )
338 337
339 338 def test_priority():
340 339 @annotate(annotate='annotate', kwarg='annotate')
341 340 def f(kwarg='default', annotate='default', default='default'):
342 341 pass
343 342
344 343 c = interactive(f, kwarg='kwarg')
345 344 check_widgets(c,
346 345 kwarg=dict(
347 346 cls=widgets.Text,
348 347 value='kwarg',
349 348 ),
350 349 annotate=dict(
351 350 cls=widgets.Text,
352 351 value='annotate',
353 352 ),
354 353 )
355 354
356 355 @nt.with_setup(clear_display)
357 356 def test_decorator_kwarg():
358 357 with tt.monkeypatch(interaction, 'display', record_display):
359 358 @interact(a=5)
360 359 def foo(a):
361 360 pass
362 361 nt.assert_equal(len(displayed), 1)
363 362 w = displayed[0].children[0]
364 363 check_widget(w,
365 364 cls=widgets.IntSlider,
366 365 value=5,
367 366 )
368 367
369 368 @nt.with_setup(clear_display)
370 369 def test_decorator_no_call():
371 370 with tt.monkeypatch(interaction, 'display', record_display):
372 371 @interact
373 372 def foo(a='default'):
374 373 pass
375 374 nt.assert_equal(len(displayed), 1)
376 375 w = displayed[0].children[0]
377 376 check_widget(w,
378 377 cls=widgets.Text,
379 378 value='default',
380 379 )
381 380
382 381 @nt.with_setup(clear_display)
383 382 def test_call_interact():
384 383 def foo(a='default'):
385 384 pass
386 385 with tt.monkeypatch(interaction, 'display', record_display):
387 386 ifoo = interact(foo)
388 387 nt.assert_equal(len(displayed), 1)
389 388 w = displayed[0].children[0]
390 389 check_widget(w,
391 390 cls=widgets.Text,
392 391 value='default',
393 392 )
394 393
395 394 @nt.with_setup(clear_display)
396 395 def test_call_interact_kwargs():
397 396 def foo(a='default'):
398 397 pass
399 398 with tt.monkeypatch(interaction, 'display', record_display):
400 399 ifoo = interact(foo, a=10)
401 400 nt.assert_equal(len(displayed), 1)
402 401 w = displayed[0].children[0]
403 402 check_widget(w,
404 403 cls=widgets.IntSlider,
405 404 value=10,
406 405 )
407 406
408 407 @nt.with_setup(clear_display)
409 408 def test_call_decorated_on_trait_change():
410 409 """test calling @interact decorated functions"""
411 410 d = {}
412 411 with tt.monkeypatch(interaction, 'display', record_display):
413 412 @interact
414 413 def foo(a='default'):
415 414 d['a'] = a
416 415 return a
417 416 nt.assert_equal(len(displayed), 1)
418 417 w = displayed[0].children[0]
419 418 check_widget(w,
420 419 cls=widgets.Text,
421 420 value='default',
422 421 )
423 422 # test calling the function directly
424 423 a = foo('hello')
425 424 nt.assert_equal(a, 'hello')
426 425 nt.assert_equal(d['a'], 'hello')
427 426
428 427 # test that setting trait values calls the function
429 428 w.value = 'called'
430 429 nt.assert_equal(d['a'], 'called')
431 430
432 431 @nt.with_setup(clear_display)
433 432 def test_call_decorated_kwargs_on_trait_change():
434 433 """test calling @interact(foo=bar) decorated functions"""
435 434 d = {}
436 435 with tt.monkeypatch(interaction, 'display', record_display):
437 436 @interact(a='kwarg')
438 437 def foo(a='default'):
439 438 d['a'] = a
440 439 return a
441 440 nt.assert_equal(len(displayed), 1)
442 441 w = displayed[0].children[0]
443 442 check_widget(w,
444 443 cls=widgets.Text,
445 444 value='kwarg',
446 445 )
447 446 # test calling the function directly
448 447 a = foo('hello')
449 448 nt.assert_equal(a, 'hello')
450 449 nt.assert_equal(d['a'], 'hello')
451 450
452 451 # test that setting trait values calls the function
453 452 w.value = 'called'
454 453 nt.assert_equal(d['a'], 'called')
455 454
456 455 def test_fixed():
457 456 c = interactive(f, a=widgets.fixed(5), b='text')
458 457 nt.assert_equal(len(c.children), 1)
459 458 w = c.children[0]
460 459 check_widget(w,
461 460 cls=widgets.Text,
462 461 value='text',
463 462 description='b',
464 463 )
465 464
466 465 def test_default_description():
467 466 c = interactive(f, b='text')
468 467 w = c.children[0]
469 468 check_widget(w,
470 469 cls=widgets.Text,
471 470 value='text',
472 471 description='b',
473 472 )
474 473
475 474 def test_custom_description():
476 475 c = interactive(f, b=widgets.Text(value='text', description='foo'))
477 476 w = c.children[0]
478 477 check_widget(w,
479 478 cls=widgets.Text,
480 479 value='text',
481 480 description='foo',
482 481 )
483 482
484 483 def test_interact_manual_button():
485 484 c = interactive(f, __manual=True)
486 485 w = c.children[0]
487 486 check_widget(w, cls=widgets.Button)
488 487
489 488 def test_interact_manual_nocall():
490 489 callcount = 0
491 490 def calltest(testarg):
492 491 callcount += 1
493 492 c = interactive(calltest, testarg=5, __manual=True)
494 493 c.children[0].value = 10
495 494 nt.assert_equal(callcount, 0)
496 495
497 496 def test_int_range_logic():
498 497 irsw = widgets.IntRangeSlider
499 498 w = irsw(value=(2, 4), min=0, max=6)
500 499 check_widget(w, cls=irsw, value=(2, 4), min=0, max=6)
501 500 w.value = (4, 2)
502 501 check_widget(w, cls=irsw, value=(2, 4), min=0, max=6)
503 502 w.value = (-1, 7)
504 503 check_widget(w, cls=irsw, value=(0, 6), min=0, max=6)
505 504 w.min = 3
506 505 check_widget(w, cls=irsw, value=(3, 6), min=3, max=6)
507 506 w.max = 3
508 507 check_widget(w, cls=irsw, value=(3, 3), min=3, max=3)
509 508
510 509 w.min = 0
511 510 w.max = 6
512 511 w.lower = 2
513 512 w.upper = 4
514 513 check_widget(w, cls=irsw, value=(2, 4), min=0, max=6)
515 514 w.value = (0, 1) #lower non-overlapping range
516 515 check_widget(w, cls=irsw, value=(0, 1), min=0, max=6)
517 516 w.value = (5, 6) #upper non-overlapping range
518 517 check_widget(w, cls=irsw, value=(5, 6), min=0, max=6)
519 518 w.value = (-1, 4) #semi out-of-range
520 519 check_widget(w, cls=irsw, value=(0, 4), min=0, max=6)
521 520 w.lower = 2
522 521 check_widget(w, cls=irsw, value=(2, 4), min=0, max=6)
523 522 w.value = (-2, -1) #wholly out of range
524 523 check_widget(w, cls=irsw, value=(0, 0), min=0, max=6)
525 524 w.value = (7, 8)
526 525 check_widget(w, cls=irsw, value=(6, 6), min=0, max=6)
527 526
528 527 with nt.assert_raises(ValueError):
529 528 w.min = 7
530 529 with nt.assert_raises(ValueError):
531 530 w.max = -1
532 531 with nt.assert_raises(ValueError):
533 532 w.lower = 5
534 533 with nt.assert_raises(ValueError):
535 534 w.upper = 1
536 535
537 536 w = irsw(min=2, max=3)
538 537 check_widget(w, min=2, max=3)
539 538 w = irsw(min=100, max=200)
540 539 check_widget(w, lower=125, upper=175, value=(125, 175))
541 540
542 541 with nt.assert_raises(ValueError):
543 542 irsw(value=(2, 4), lower=3)
544 543 with nt.assert_raises(ValueError):
545 544 irsw(value=(2, 4), upper=3)
546 545 with nt.assert_raises(ValueError):
547 546 irsw(value=(2, 4), lower=3, upper=3)
548 547 with nt.assert_raises(ValueError):
549 548 irsw(min=2, max=1)
550 549 with nt.assert_raises(ValueError):
551 550 irsw(lower=5)
552 551 with nt.assert_raises(ValueError):
553 552 irsw(upper=5)
554 553
555 554
556 555 def test_float_range_logic():
557 556 frsw = widgets.FloatRangeSlider
558 557 w = frsw(value=(.2, .4), min=0., max=.6)
559 558 check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6)
560 559 w.value = (.4, .2)
561 560 check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6)
562 561 w.value = (-.1, .7)
563 562 check_widget(w, cls=frsw, value=(0., .6), min=0., max=.6)
564 563 w.min = .3
565 564 check_widget(w, cls=frsw, value=(.3, .6), min=.3, max=.6)
566 565 w.max = .3
567 566 check_widget(w, cls=frsw, value=(.3, .3), min=.3, max=.3)
568 567
569 568 w.min = 0.
570 569 w.max = .6
571 570 w.lower = .2
572 571 w.upper = .4
573 572 check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6)
574 573 w.value = (0., .1) #lower non-overlapping range
575 574 check_widget(w, cls=frsw, value=(0., .1), min=0., max=.6)
576 575 w.value = (.5, .6) #upper non-overlapping range
577 576 check_widget(w, cls=frsw, value=(.5, .6), min=0., max=.6)
578 577 w.value = (-.1, .4) #semi out-of-range
579 578 check_widget(w, cls=frsw, value=(0., .4), min=0., max=.6)
580 579 w.lower = .2
581 580 check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6)
582 581 w.value = (-.2, -.1) #wholly out of range
583 582 check_widget(w, cls=frsw, value=(0., 0.), min=0., max=.6)
584 583 w.value = (.7, .8)
585 584 check_widget(w, cls=frsw, value=(.6, .6), min=.0, max=.6)
586 585
587 586 with nt.assert_raises(ValueError):
588 587 w.min = .7
589 588 with nt.assert_raises(ValueError):
590 589 w.max = -.1
591 590 with nt.assert_raises(ValueError):
592 591 w.lower = .5
593 592 with nt.assert_raises(ValueError):
594 593 w.upper = .1
595 594
596 595 w = frsw(min=2, max=3)
597 596 check_widget(w, min=2, max=3)
598 597 w = frsw(min=1., max=2.)
599 598 check_widget(w, lower=1.25, upper=1.75, value=(1.25, 1.75))
600 599
601 600 with nt.assert_raises(ValueError):
602 601 frsw(value=(2, 4), lower=3)
603 602 with nt.assert_raises(ValueError):
604 603 frsw(value=(2, 4), upper=3)
605 604 with nt.assert_raises(ValueError):
606 605 frsw(value=(2, 4), lower=3, upper=3)
607 606 with nt.assert_raises(ValueError):
608 607 frsw(min=.2, max=.1)
609 608 with nt.assert_raises(ValueError):
610 609 frsw(lower=5)
611 610 with nt.assert_raises(ValueError):
612 611 frsw(upper=5)
General Comments 0
You need to be logged in to leave comments. Login now