##// END OF EJS Templates
extend buffer len
Matthias Bussonnier -
Show More
@@ -1,567 +1,568 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
373 373 child = pexpect.spawn(
374 374 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
375 375 )
376 376 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
377 377
378 378 child.expect("IPython")
379 379 child.expect("\n")
380 380
381 381 child.timeout = 5 * IPYTHON_TESTING_TIMEOUT_SCALE
382 child.str_last_chars = 500
382 383
383 384 dedented_blocks = [dedent(b).strip() for b in skip_decorators_blocks]
384 385 in_prompt_number = 1
385 386 for cblock in dedented_blocks:
386 387 child.expect_exact(f"In [{in_prompt_number}]:")
387 388 in_prompt_number += 1
388 389 for line in cblock.splitlines():
389 390 child.sendline(line)
390 391 child.expect_exact(line)
391 392 child.sendline("")
392 393 return child
393 394
394 395
395 396 @skip_win32
396 397 def test_decorator_skip():
397 398 """test that decorator frames can be skipped."""
398 399
399 400 child = _decorator_skip_setup()
400 401
401 402 child.expect_exact("3 bar(3, 4)")
402 403 child.expect("ipdb>")
403 404
404 405 child.expect("ipdb>")
405 406 child.sendline("step")
406 407 child.expect_exact("step")
407 408
408 409 child.expect_exact("1 @pdb_skipped_decorator")
409 410
410 411 child.sendline("s")
411 412 child.expect_exact("return x * y")
412 413
413 414 child.close()
414 415
415 416
416 417 @pytest.mark.skipif(platform.python_implementation() == "PyPy", reason="issues on PyPy")
417 418 @skip_win32
418 419 def test_decorator_skip_disabled():
419 420 """test that decorator frame skipping can be disabled"""
420 421
421 422 child = _decorator_skip_setup()
422 423
423 424 child.expect_exact("3 bar(3, 4)")
424 425
425 426 for input_, expected in [
426 427 ("skip_predicates debuggerskip False", ""),
427 428 ("skip_predicates", "debuggerskip : False"),
428 429 ("step", "---> 2 def wrapped_fn"),
429 430 ("step", "----> 3 __debuggerskip__"),
430 431 ("step", "----> 4 helper_1()"),
431 432 ("step", "---> 1 def helper_1():"),
432 433 ("next", "----> 2 helpers_helper()"),
433 434 ("next", "--Return--"),
434 435 ("next", "----> 5 __debuggerskip__ = False"),
435 436 ]:
436 437 child.expect("ipdb>")
437 438 child.sendline(input_)
438 439 child.expect_exact(input_)
439 440 child.expect_exact(expected)
440 441
441 442 child.close()
442 443
443 444
444 445 @pytest.mark.skipif(platform.python_implementation() == "PyPy", reason="issues on PyPy")
445 446 @skip_win32
446 447 def test_decorator_skip_with_breakpoint():
447 448 """test that decorator frame skipping can be disabled"""
448 449
449 450 import pexpect
450 451
451 452 env = os.environ.copy()
452 453 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
453 454
454 455 child = pexpect.spawn(
455 456 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
456 457 )
457 458 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
458 459
459 460 child.expect("IPython")
460 461 child.expect("\n")
461 462
462 463 child.timeout = 5 * IPYTHON_TESTING_TIMEOUT_SCALE
463 464
464 465 ### we need a filename, so we need to exec the full block with a filename
465 466 with NamedTemporaryFile(suffix=".py", dir=".", delete=True) as tf:
466 467
467 468 name = tf.name[:-3].split("/")[-1]
468 469 tf.write("\n".join([dedent(x) for x in skip_decorators_blocks[:-1]]).encode())
469 470 tf.flush()
470 471 codeblock = f"from {name} import f"
471 472
472 473 dedented_blocks = [
473 474 codeblock,
474 475 "f()",
475 476 ]
476 477
477 478 in_prompt_number = 1
478 479 for cblock in dedented_blocks:
479 480 child.expect_exact(f"In [{in_prompt_number}]:")
480 481 in_prompt_number += 1
481 482 for line in cblock.splitlines():
482 483 child.sendline(line)
483 484 child.expect_exact(line)
484 485 child.sendline("")
485 486
486 487 # as the filename does not exists, we'll rely on the filename prompt
487 488 child.expect_exact("47 bar(3, 4)")
488 489
489 490 for input_, expected in [
490 491 (f"b {name}.py:3", ""),
491 492 ("step", "1---> 3 pass # should not stop here except"),
492 493 ("step", "---> 38 @pdb_skipped_decorator"),
493 494 ("continue", ""),
494 495 ]:
495 496 child.expect("ipdb>")
496 497 child.sendline(input_)
497 498 child.expect_exact(input_)
498 499 child.expect_exact(expected)
499 500
500 501 child.close()
501 502
502 503
503 504 @skip_win32
504 505 def test_where_erase_value():
505 506 """Test that `where` does not access f_locals and erase values."""
506 507 import pexpect
507 508
508 509 env = os.environ.copy()
509 510 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
510 511
511 512 child = pexpect.spawn(
512 513 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
513 514 )
514 515 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
515 516
516 517 child.expect("IPython")
517 518 child.expect("\n")
518 519 child.expect_exact("In [1]")
519 520
520 521 block = dedent(
521 522 """
522 523 def simple_f():
523 524 myvar = 1
524 525 print(myvar)
525 526 1/0
526 527 print(myvar)
527 528 simple_f() """
528 529 )
529 530
530 531 for line in block.splitlines():
531 532 child.sendline(line)
532 533 child.expect_exact(line)
533 534 child.expect_exact("ZeroDivisionError")
534 535 child.expect_exact("In [2]:")
535 536
536 537 child.sendline("%debug")
537 538
538 539 ##
539 540 child.expect("ipdb>")
540 541
541 542 child.sendline("myvar")
542 543 child.expect("1")
543 544
544 545 ##
545 546 child.expect("ipdb>")
546 547
547 548 child.sendline("myvar = 2")
548 549
549 550 ##
550 551 child.expect_exact("ipdb>")
551 552
552 553 child.sendline("myvar")
553 554
554 555 child.expect_exact("2")
555 556
556 557 ##
557 558 child.expect("ipdb>")
558 559 child.sendline("where")
559 560
560 561 ##
561 562 child.expect("ipdb>")
562 563 child.sendline("myvar")
563 564
564 565 child.expect_exact("2")
565 566 child.expect("ipdb>")
566 567
567 568 child.close()
General Comments 0
You need to be logged in to leave comments. Login now