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