##// END OF EJS Templates
debug
Matthias Bussonnier -
Show More
@@ -1,571 +1,574 b''
1 1 """Tests for debugging machinery.
2 2 """
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 7 import bdb
8 8 import builtins
9 9 import os
10 10 import sys
11 11 import platform
12 12
13 13 from tempfile import NamedTemporaryFile
14 14 from textwrap import dedent
15 15 from unittest.mock import patch
16 16
17 17 from IPython.core import debugger
18 18 from IPython.testing import IPYTHON_TESTING_TIMEOUT_SCALE
19 19 from IPython.testing.decorators import skip_win32
20 20 import pytest
21 21
22 22 #-----------------------------------------------------------------------------
23 23 # Helper classes, from CPython's Pdb test suite
24 24 #-----------------------------------------------------------------------------
25 25
26 26 class _FakeInput(object):
27 27 """
28 28 A fake input stream for pdb's interactive debugger. Whenever a
29 29 line is read, print it (to simulate the user typing it), and then
30 30 return it. The set of lines to return is specified in the
31 31 constructor; they should not have trailing newlines.
32 32 """
33 33 def __init__(self, lines):
34 34 self.lines = iter(lines)
35 35
36 36 def readline(self):
37 37 line = next(self.lines)
38 38 print(line)
39 39 return line+'\n'
40 40
41 41 class PdbTestInput(object):
42 42 """Context manager that makes testing Pdb in doctests easier."""
43 43
44 44 def __init__(self, input):
45 45 self.input = input
46 46
47 47 def __enter__(self):
48 48 self.real_stdin = sys.stdin
49 49 sys.stdin = _FakeInput(self.input)
50 50
51 51 def __exit__(self, *exc):
52 52 sys.stdin = self.real_stdin
53 53
54 54 #-----------------------------------------------------------------------------
55 55 # Tests
56 56 #-----------------------------------------------------------------------------
57 57
58 58 def test_ipdb_magics():
59 59 '''Test calling some IPython magics from ipdb.
60 60
61 61 First, set up some test functions and classes which we can inspect.
62 62
63 63 >>> class ExampleClass(object):
64 64 ... """Docstring for ExampleClass."""
65 65 ... def __init__(self):
66 66 ... """Docstring for ExampleClass.__init__"""
67 67 ... pass
68 68 ... def __str__(self):
69 69 ... return "ExampleClass()"
70 70
71 71 >>> def example_function(x, y, z="hello"):
72 72 ... """Docstring for example_function."""
73 73 ... pass
74 74
75 75 >>> old_trace = sys.gettrace()
76 76
77 77 Create a function which triggers ipdb.
78 78
79 79 >>> def trigger_ipdb():
80 80 ... a = ExampleClass()
81 81 ... debugger.Pdb().set_trace()
82 82
83 83 >>> with PdbTestInput([
84 84 ... 'pdef example_function',
85 85 ... 'pdoc ExampleClass',
86 86 ... 'up',
87 87 ... 'down',
88 88 ... 'list',
89 89 ... 'pinfo a',
90 90 ... 'll',
91 91 ... 'continue',
92 92 ... ]):
93 93 ... trigger_ipdb()
94 94 --Return--
95 95 None
96 96 > <doctest ...>(3)trigger_ipdb()
97 97 1 def trigger_ipdb():
98 98 2 a = ExampleClass()
99 99 ----> 3 debugger.Pdb().set_trace()
100 100 <BLANKLINE>
101 101 ipdb> pdef example_function
102 102 example_function(x, y, z='hello')
103 103 ipdb> pdoc ExampleClass
104 104 Class docstring:
105 105 Docstring for ExampleClass.
106 106 Init docstring:
107 107 Docstring for ExampleClass.__init__
108 108 ipdb> up
109 109 > <doctest ...>(11)<module>()
110 110 7 'pinfo a',
111 111 8 'll',
112 112 9 'continue',
113 113 10 ]):
114 114 ---> 11 trigger_ipdb()
115 115 <BLANKLINE>
116 116 ipdb> down
117 117 None
118 118 > <doctest ...>(3)trigger_ipdb()
119 119 1 def trigger_ipdb():
120 120 2 a = ExampleClass()
121 121 ----> 3 debugger.Pdb().set_trace()
122 122 <BLANKLINE>
123 123 ipdb> list
124 124 1 def trigger_ipdb():
125 125 2 a = ExampleClass()
126 126 ----> 3 debugger.Pdb().set_trace()
127 127 <BLANKLINE>
128 128 ipdb> pinfo a
129 129 Type: ExampleClass
130 130 String form: ExampleClass()
131 131 Namespace: Local...
132 132 Docstring: Docstring for ExampleClass.
133 133 Init docstring: Docstring for ExampleClass.__init__
134 134 ipdb> ll
135 135 1 def trigger_ipdb():
136 136 2 a = ExampleClass()
137 137 ----> 3 debugger.Pdb().set_trace()
138 138 <BLANKLINE>
139 139 ipdb> continue
140 140
141 141 Restore previous trace function, e.g. for coverage.py
142 142
143 143 >>> sys.settrace(old_trace)
144 144 '''
145 145
146 146 def test_ipdb_magics2():
147 147 '''Test ipdb with a very short function.
148 148
149 149 >>> old_trace = sys.gettrace()
150 150
151 151 >>> def bar():
152 152 ... pass
153 153
154 154 Run ipdb.
155 155
156 156 >>> with PdbTestInput([
157 157 ... 'continue',
158 158 ... ]):
159 159 ... debugger.Pdb().runcall(bar)
160 160 > <doctest ...>(2)bar()
161 161 1 def bar():
162 162 ----> 2 pass
163 163 <BLANKLINE>
164 164 ipdb> continue
165 165
166 166 Restore previous trace function, e.g. for coverage.py
167 167
168 168 >>> sys.settrace(old_trace)
169 169 '''
170 170
171 171 def can_quit():
172 172 '''Test that quit work in ipydb
173 173
174 174 >>> old_trace = sys.gettrace()
175 175
176 176 >>> def bar():
177 177 ... pass
178 178
179 179 >>> with PdbTestInput([
180 180 ... 'quit',
181 181 ... ]):
182 182 ... debugger.Pdb().runcall(bar)
183 183 > <doctest ...>(2)bar()
184 184 1 def bar():
185 185 ----> 2 pass
186 186 <BLANKLINE>
187 187 ipdb> quit
188 188
189 189 Restore previous trace function, e.g. for coverage.py
190 190
191 191 >>> sys.settrace(old_trace)
192 192 '''
193 193
194 194
195 195 def can_exit():
196 196 '''Test that quit work in ipydb
197 197
198 198 >>> old_trace = sys.gettrace()
199 199
200 200 >>> def bar():
201 201 ... pass
202 202
203 203 >>> with PdbTestInput([
204 204 ... 'exit',
205 205 ... ]):
206 206 ... debugger.Pdb().runcall(bar)
207 207 > <doctest ...>(2)bar()
208 208 1 def bar():
209 209 ----> 2 pass
210 210 <BLANKLINE>
211 211 ipdb> exit
212 212
213 213 Restore previous trace function, e.g. for coverage.py
214 214
215 215 >>> sys.settrace(old_trace)
216 216 '''
217 217
218 218
219 219 def test_interruptible_core_debugger():
220 220 """The debugger can be interrupted.
221 221
222 222 The presumption is there is some mechanism that causes a KeyboardInterrupt
223 223 (this is implemented in ipykernel). We want to ensure the
224 224 KeyboardInterrupt cause debugging to cease.
225 225 """
226 226 def raising_input(msg="", called=[0]):
227 227 called[0] += 1
228 228 assert called[0] == 1, "input() should only be called once!"
229 229 raise KeyboardInterrupt()
230 230
231 231 tracer_orig = sys.gettrace()
232 232 try:
233 233 with patch.object(builtins, "input", raising_input):
234 234 debugger.InterruptiblePdb().set_trace()
235 235 # The way this test will fail is by set_trace() never exiting,
236 236 # resulting in a timeout by the test runner. The alternative
237 237 # implementation would involve a subprocess, but that adds issues
238 238 # with interrupting subprocesses that are rather complex, so it's
239 239 # simpler just to do it this way.
240 240 finally:
241 241 # restore the original trace function
242 242 sys.settrace(tracer_orig)
243 243
244 244
245 245 @skip_win32
246 246 def test_xmode_skip():
247 247 """that xmode skip frames
248 248
249 249 Not as a doctest as pytest does not run doctests.
250 250 """
251 251 import pexpect
252 252 env = os.environ.copy()
253 253 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
254 254
255 255 child = pexpect.spawn(
256 256 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
257 257 )
258 258 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
259 259
260 260 child.expect("IPython")
261 261 child.expect("\n")
262 262 child.expect_exact("In [1]")
263 263
264 264 block = dedent(
265 265 """
266 266 def f():
267 267 __tracebackhide__ = True
268 268 g()
269 269
270 270 def g():
271 271 raise ValueError
272 272
273 273 f()
274 274 """
275 275 )
276 276
277 277 for line in block.splitlines():
278 278 child.sendline(line)
279 279 child.expect_exact(line)
280 280 child.expect_exact("skipping")
281 281
282 282 block = dedent(
283 283 """
284 284 def f():
285 285 __tracebackhide__ = True
286 286 g()
287 287
288 288 def g():
289 289 from IPython.core.debugger import set_trace
290 290 set_trace()
291 291
292 292 f()
293 293 """
294 294 )
295 295
296 296 for line in block.splitlines():
297 297 child.sendline(line)
298 298 child.expect_exact(line)
299 299
300 300 child.expect("ipdb>")
301 301 child.sendline("w")
302 302 child.expect("hidden")
303 303 child.expect("ipdb>")
304 304 child.sendline("skip_hidden false")
305 305 child.sendline("w")
306 306 child.expect("__traceba")
307 307 child.expect("ipdb>")
308 308
309 309 child.close()
310 310
311 311
312 312 skip_decorators_blocks = (
313 313 """
314 314 def helpers_helper():
315 315 pass # should not stop here except breakpoint
316 316 """,
317 317 """
318 318 def helper_1():
319 319 helpers_helper() # should not stop here
320 320 """,
321 321 """
322 322 def helper_2():
323 323 pass # should not stop here
324 324 """,
325 325 """
326 326 def pdb_skipped_decorator2(function):
327 327 def wrapped_fn(*args, **kwargs):
328 328 __debuggerskip__ = True
329 329 helper_2()
330 330 __debuggerskip__ = False
331 331 result = function(*args, **kwargs)
332 332 __debuggerskip__ = True
333 333 helper_2()
334 334 return result
335 335 return wrapped_fn
336 336 """,
337 337 """
338 338 def pdb_skipped_decorator(function):
339 339 def wrapped_fn(*args, **kwargs):
340 340 __debuggerskip__ = True
341 341 helper_1()
342 342 __debuggerskip__ = False
343 343 result = function(*args, **kwargs)
344 344 __debuggerskip__ = True
345 345 helper_2()
346 346 return result
347 347 return wrapped_fn
348 348 """,
349 349 """
350 350 @pdb_skipped_decorator
351 351 @pdb_skipped_decorator2
352 352 def bar(x, y):
353 353 return x * y
354 354 """,
355 355 """import IPython.terminal.debugger as ipdb""",
356 356 """
357 357 def f():
358 358 ipdb.set_trace()
359 359 bar(3, 4)
360 360 """,
361 361 """
362 362 f()
363 363 """,
364 364 )
365 365
366 366
367 367 def _decorator_skip_setup():
368 368 import pexpect
369 369
370 370 env = os.environ.copy()
371 371 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
372 372 env["PROMPT_TOOLKIT_NO_CPR"] = "1"
373 373
374 374 child = pexpect.spawn(
375 375 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
376 376 )
377 377 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
378 378
379 379 child.expect("IPython")
380 380 child.expect("\n")
381 381
382 382 child.timeout = 5 * IPYTHON_TESTING_TIMEOUT_SCALE
383 383 child.str_last_chars = 500
384 384
385 385 dedented_blocks = [dedent(b).strip() for b in skip_decorators_blocks]
386 386 in_prompt_number = 1
387 387 for cblock in dedented_blocks:
388 388 child.expect_exact(f"In [{in_prompt_number}]:")
389 389 in_prompt_number += 1
390 390 for line in cblock.splitlines():
391 391 child.sendline(line)
392 392 child.expect_exact(line)
393 393 child.sendline("")
394 394 return child
395 395
396 396
397 397 @skip_win32
398 398 def test_decorator_skip():
399 399 """test that decorator frames can be skipped."""
400 400
401 401 child = _decorator_skip_setup()
402 402
403 child.expect_exact("ipython-input-8")
403 404 child.expect_exact("3 bar(3, 4)")
404 405 child.expect("ipdb>")
405 406
406 407 child.expect("ipdb>")
407 408 child.sendline("step")
408 409 child.expect_exact("step")
410 child.expect_exact("--Call--")
411 child.expect_exact("ipython-input-6")
409 412
410 413 child.expect_exact("1 @pdb_skipped_decorator")
411 414
412 415 child.sendline("s")
413 416 child.expect_exact("return x * y")
414 417
415 418 child.close()
416 419
417 420
418 421 @pytest.mark.skipif(platform.python_implementation() == "PyPy", reason="issues on PyPy")
419 422 @skip_win32
420 423 def test_decorator_skip_disabled():
421 424 """test that decorator frame skipping can be disabled"""
422 425
423 426 child = _decorator_skip_setup()
424 427
425 428 child.expect_exact("3 bar(3, 4)")
426 429
427 430 for input_, expected in [
428 431 ("skip_predicates debuggerskip False", ""),
429 432 ("skip_predicates", "debuggerskip : False"),
430 433 ("step", "---> 2 def wrapped_fn"),
431 434 ("step", "----> 3 __debuggerskip__"),
432 435 ("step", "----> 4 helper_1()"),
433 436 ("step", "---> 1 def helper_1():"),
434 437 ("next", "----> 2 helpers_helper()"),
435 438 ("next", "--Return--"),
436 439 ("next", "----> 5 __debuggerskip__ = False"),
437 440 ]:
438 441 child.expect("ipdb>")
439 442 child.sendline(input_)
440 443 child.expect_exact(input_)
441 444 child.expect_exact(expected)
442 445
443 446 child.close()
444 447
445 448
446 449 @pytest.mark.skipif(platform.python_implementation() == "PyPy", reason="issues on PyPy")
447 450 @skip_win32
448 451 def test_decorator_skip_with_breakpoint():
449 452 """test that decorator frame skipping can be disabled"""
450 453
451 454 import pexpect
452 455
453 456 env = os.environ.copy()
454 457 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
455 458 env["PROMPT_TOOLKIT_NO_CPR"] = "1"
456 459
457 460 child = pexpect.spawn(
458 461 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
459 462 )
460 463 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
461 464 child.str_last_chars = 500
462 465
463 466 child.expect("IPython")
464 467 child.expect("\n")
465 468
466 469 child.timeout = 5 * IPYTHON_TESTING_TIMEOUT_SCALE
467 470
468 471 ### we need a filename, so we need to exec the full block with a filename
469 472 with NamedTemporaryFile(suffix=".py", dir=".", delete=True) as tf:
470 473
471 474 name = tf.name[:-3].split("/")[-1]
472 475 tf.write("\n".join([dedent(x) for x in skip_decorators_blocks[:-1]]).encode())
473 476 tf.flush()
474 477 codeblock = f"from {name} import f"
475 478
476 479 dedented_blocks = [
477 480 codeblock,
478 481 "f()",
479 482 ]
480 483
481 484 in_prompt_number = 1
482 485 for cblock in dedented_blocks:
483 486 child.expect_exact(f"In [{in_prompt_number}]:")
484 487 in_prompt_number += 1
485 488 for line in cblock.splitlines():
486 489 child.sendline(line)
487 490 child.expect_exact(line)
488 491 child.sendline("")
489 492
490 493 # as the filename does not exists, we'll rely on the filename prompt
491 494 child.expect_exact("47 bar(3, 4)")
492 495
493 496 for input_, expected in [
494 497 (f"b {name}.py:3", ""),
495 498 ("step", "1---> 3 pass # should not stop here except"),
496 499 ("step", "---> 38 @pdb_skipped_decorator"),
497 500 ("continue", ""),
498 501 ]:
499 502 child.expect("ipdb>")
500 503 child.sendline(input_)
501 504 child.expect_exact(input_)
502 505 child.expect_exact(expected)
503 506
504 507 child.close()
505 508
506 509
507 510 @skip_win32
508 511 def test_where_erase_value():
509 512 """Test that `where` does not access f_locals and erase values."""
510 513 import pexpect
511 514
512 515 env = os.environ.copy()
513 516 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
514 517
515 518 child = pexpect.spawn(
516 519 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
517 520 )
518 521 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
519 522
520 523 child.expect("IPython")
521 524 child.expect("\n")
522 525 child.expect_exact("In [1]")
523 526
524 527 block = dedent(
525 528 """
526 529 def simple_f():
527 530 myvar = 1
528 531 print(myvar)
529 532 1/0
530 533 print(myvar)
531 534 simple_f() """
532 535 )
533 536
534 537 for line in block.splitlines():
535 538 child.sendline(line)
536 539 child.expect_exact(line)
537 540 child.expect_exact("ZeroDivisionError")
538 541 child.expect_exact("In [2]:")
539 542
540 543 child.sendline("%debug")
541 544
542 545 ##
543 546 child.expect("ipdb>")
544 547
545 548 child.sendline("myvar")
546 549 child.expect("1")
547 550
548 551 ##
549 552 child.expect("ipdb>")
550 553
551 554 child.sendline("myvar = 2")
552 555
553 556 ##
554 557 child.expect_exact("ipdb>")
555 558
556 559 child.sendline("myvar")
557 560
558 561 child.expect_exact("2")
559 562
560 563 ##
561 564 child.expect("ipdb>")
562 565 child.sendline("where")
563 566
564 567 ##
565 568 child.expect("ipdb>")
566 569 child.sendline("myvar")
567 570
568 571 child.expect_exact("2")
569 572 child.expect("ipdb>")
570 573
571 574 child.close()
General Comments 0
You need to be logged in to leave comments. Login now