| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 5475253 commit d545869
4 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -373,7 +373,7 @@ def g(count=10): | |||
| 373 | 373 | ' return g(count-1)\n' | |
| 374 | 374 | f' File "{__file__}", line {lineno_g+2}, in g\n' | |
| 375 | 375 | ' return g(count-1)\n' | |
| 376 | - ' [Previous line repeated 6 more times]\n' | ||
| 376 | + ' [Previous line repeated 7 more times]\n' | ||
| 377 | 377 | f' File "{__file__}", line {lineno_g+3}, in g\n' | |
| 378 | 378 | ' raise ValueError\n' | |
| 379 | 379 | 'ValueError\n' | |
@@ -412,14 +412,71 @@ def h(count=10): | |||
| 412 | 412 | ' return h(count-1)\n' | |
| 413 | 413 | f' File "{__file__}", line {lineno_h+2}, in h\n' | |
| 414 | 414 | ' return h(count-1)\n' | |
| 415 | - ' [Previous line repeated 6 more times]\n' | ||
| 415 | + ' [Previous line repeated 7 more times]\n' | ||
| 416 | 416 | f' File "{__file__}", line {lineno_h+3}, in h\n' | |
| 417 | 417 | ' g()\n' | |
| 418 | 418 | ) | |
| 419 | 419 | expected = (result_h + result_g).splitlines() | |
| 420 | 420 | actual = stderr_h.getvalue().splitlines() | |
| 421 | 421 | self.assertEqual(actual, expected) | |
| 422 | 422 | ||
| 423 | + # Check the boundary conditions. First, test just below the cutoff. | ||
| 424 | + with captured_output("stderr") as stderr_g: | ||
| 425 | + try: | ||
| 426 | + g(traceback._RECURSIVE_CUTOFF) | ||
| 427 | + except ValueError as exc: | ||
| 428 | + render_exc() | ||
| 429 | + else: | ||
| 430 | + self.fail("no error raised") | ||
| 431 | + result_g = ( | ||
| 432 | + f' File "{__file__}", line {lineno_g+2}, in g\n' | ||
| 433 | + ' return g(count-1)\n' | ||
| 434 | + f' File "{__file__}", line {lineno_g+2}, in g\n' | ||
| 435 | + ' return g(count-1)\n' | ||
| 436 | + f' File "{__file__}", line {lineno_g+2}, in g\n' | ||
| 437 | + ' return g(count-1)\n' | ||
| 438 | + f' File "{__file__}", line {lineno_g+3}, in g\n' | ||
| 439 | + ' raise ValueError\n' | ||
| 440 | + 'ValueError\n' | ||
| 441 | + ) | ||
| 442 | + tb_line = ( | ||
| 443 | + 'Traceback (most recent call last):\n' | ||
| 444 | + f' File "{__file__}", line {lineno_g+71}, in _check_recursive_traceback_display\n' | ||
| 445 | + ' g(traceback._RECURSIVE_CUTOFF)\n' | ||
| 446 | + ) | ||
| 447 | + expected = (tb_line + result_g).splitlines() | ||
| 448 | + actual = stderr_g.getvalue().splitlines() | ||
| 449 | + self.assertEqual(actual, expected) | ||
| 450 | + | ||
| 451 | + # Second, test just above the cutoff. | ||
| 452 | + with captured_output("stderr") as stderr_g: | ||
| 453 | + try: | ||
| 454 | + g(traceback._RECURSIVE_CUTOFF + 1) | ||
| 455 | + except ValueError as exc: | ||
| 456 | + render_exc() | ||
| 457 | + else: | ||
| 458 | + self.fail("no error raised") | ||
| 459 | + result_g = ( | ||
| 460 | + f' File "{__file__}", line {lineno_g+2}, in g\n' | ||
| 461 | + ' return g(count-1)\n' | ||
| 462 | + f' File "{__file__}", line {lineno_g+2}, in g\n' | ||
| 463 | + ' return g(count-1)\n' | ||
| 464 | + f' File "{__file__}", line {lineno_g+2}, in g\n' | ||
| 465 | + ' return g(count-1)\n' | ||
| 466 | + ' [Previous line repeated 1 more time]\n' | ||
| 467 | + f' File "{__file__}", line {lineno_g+3}, in g\n' | ||
| 468 | + ' raise ValueError\n' | ||
| 469 | + 'ValueError\n' | ||
| 470 | + ) | ||
| 471 | + tb_line = ( | ||
| 472 | + 'Traceback (most recent call last):\n' | ||
| 473 | + f' File "{__file__}", line {lineno_g+99}, in _check_recursive_traceback_display\n' | ||
| 474 | + ' g(traceback._RECURSIVE_CUTOFF + 1)\n' | ||
| 475 | + ) | ||
| 476 | + expected = (tb_line + result_g).splitlines() | ||
| 477 | + actual = stderr_g.getvalue().splitlines() | ||
| 478 | + self.assertEqual(actual, expected) | ||
| 479 | + | ||
| 423 | 480 | def test_recursive_traceback_python(self): | |
| 424 | 481 | self._check_recursive_traceback_display(traceback.print_exc) | |
| 425 | 482 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -310,6 +310,8 @@ def walk_tb(tb): | |||
| 310 | 310 | tb = tb.tb_next | |
| 311 | 311 | ||
| 312 | 312 | ||
| 313 | + _RECURSIVE_CUTOFF = 3 # Also hardcoded in traceback.c. | ||
| 314 | + | ||
| 313 | 315 | class StackSummary(list): | |
| 314 | 316 | """A stack of frames.""" | |
| 315 | 317 | ||
@@ -398,18 +400,21 @@ def format(self): | |||
| 398 | 400 | last_name = None | |
| 399 | 401 | count = 0 | |
| 400 | 402 | for frame in self: | |
| 401 | - if (last_file is not None and last_file == frame.filename and | ||
| 402 | - last_line is not None and last_line == frame.lineno and | ||
| 403 | - last_name is not None and last_name == frame.name): | ||
| 404 | - count += 1 | ||
| 405 | - else: | ||
| 406 | - if count > 3: | ||
| 407 | - result.append(f' [Previous line repeated {count-3} more times]\n') | ||
| 403 | + if (last_file is None or last_file != frame.filename or | ||
| 404 | + last_line is None or last_line != frame.lineno or | ||
| 405 | + last_name is None or last_name != frame.name): | ||
| 406 | + if count > _RECURSIVE_CUTOFF: | ||
| 407 | + count -= _RECURSIVE_CUTOFF | ||
| 408 | + result.append( | ||
| 409 | + f' [Previous line repeated {count} more ' | ||
| 410 | + f'time{"s" if count > 1 else ""}]\n' | ||
| 411 | + ) | ||
| 408 | 412 | last_file = frame.filename | |
| 409 | 413 | last_line = frame.lineno | |
| 410 | 414 | last_name = frame.name | |
| 411 | 415 | count = 0 | |
| 412 | - if count >= 3: | ||
| 416 | + count += 1 | ||
| 417 | + if count > _RECURSIVE_CUTOFF: | ||
| 413 | 418 | continue | |
| 414 | 419 | row = [] | |
| 415 | 420 | row.append(' File "{}", line {}, in {}\n'.format( | |
@@ -420,8 +425,12 @@ def format(self): | |||
| 420 | 425 | for name, value in sorted(frame.locals.items()): | |
| 421 | 426 | row.append(' {name} = {value}\n'.format(name=name, value=value)) | |
| 422 | 427 | result.append(''.join(row)) | |
| 423 | - if count > 3: | ||
| 424 | - result.append(f' [Previous line repeated {count-3} more times]\n') | ||
| 428 | + if count > _RECURSIVE_CUTOFF: | ||
| 429 | + count -= _RECURSIVE_CUTOFF | ||
| 430 | + result.append( | ||
| 431 | + f' [Previous line repeated {count} more ' | ||
| 432 | + f'time{"s" if count > 1 else ""}]\n' | ||
| 433 | + ) | ||
| 425 | 434 | return result | |
| 426 | 435 | ||
| 427 | 436 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,2 @@ | |||
| 1 | + Fix an off-by-one in the recursive call pruning feature of traceback | ||
| 2 | + formatting. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -511,16 +511,21 @@ tb_displayline(PyObject *f, PyObject *filename, int lineno, PyObject *name) | |||
| 511 | 511 | return err; | |
| 512 | 512 | } | |
| 513 | 513 | ||
| 514 | + static const int TB_RECURSIVE_CUTOFF = 3; // Also hardcoded in traceback.py. | ||
| 515 | + | ||
| 514 | 516 | static int | |
| 515 | 517 | tb_print_line_repeated(PyObject *f, long cnt) | |
| 516 | 518 | { | |
| 517 | - int err; | ||
| 519 | + cnt -= TB_RECURSIVE_CUTOFF; | ||
| 518 | 520 | PyObject *line = PyUnicode_FromFormat( | |
| 519 | - " [Previous line repeated %ld more times]\n", cnt-3); | ||
| 521 | + (cnt > 1) | ||
| 522 | + ? " [Previous line repeated %ld more times]\n" | ||
| 523 | + : " [Previous line repeated %ld more time]\n", | ||
| 524 | + cnt); | ||
| 520 | 525 | if (line == NULL) { | |
| 521 | 526 | return -1; | |
| 522 | 527 | } | |
| 523 | - err = PyFile_WriteObject(line, f, Py_PRINT_RAW); | ||
| 528 | + int err = PyFile_WriteObject(line, f, Py_PRINT_RAW); | ||
| 524 | 529 | Py_DECREF(line); | |
| 525 | 530 | return err; | |
| 526 | 531 | } | |
@@ -544,23 +549,20 @@ tb_printinternal(PyTracebackObject *tb, PyObject *f, long limit) | |||
| 544 | 549 | tb = tb->tb_next; | |
| 545 | 550 | } | |
| 546 | 551 | while (tb != NULL && err == 0) { | |
| 547 | - if (last_file != NULL && | ||
| 548 | - tb->tb_frame->f_code->co_filename == last_file && | ||
| 549 | - last_line != -1 && tb->tb_lineno == last_line && | ||
| 550 | - last_name != NULL && tb->tb_frame->f_code->co_name == last_name) | ||
| 551 | - { | ||
| 552 | - cnt++; | ||
| 553 | - } | ||
| 554 | - else { | ||
| 555 | - if (cnt > 3) { | ||
| 552 | + if (last_file == NULL || | ||
| 553 | + tb->tb_frame->f_code->co_filename != last_file || | ||
| 554 | + last_line == -1 || tb->tb_lineno != last_line || | ||
| 555 | + last_name == NULL || tb->tb_frame->f_code->co_name != last_name) { | ||
| 556 | + if (cnt > TB_RECURSIVE_CUTOFF) { | ||
| 556 | 557 | err = tb_print_line_repeated(f, cnt); | |
| 557 | 558 | } | |
| 558 | 559 | last_file = tb->tb_frame->f_code->co_filename; | |
| 559 | 560 | last_line = tb->tb_lineno; | |
| 560 | 561 | last_name = tb->tb_frame->f_code->co_name; | |
| 561 | 562 | cnt = 0; | |
| 562 | 563 | } | |
| 563 | - if (err == 0 && cnt < 3) { | ||
| 564 | + cnt++; | ||
| 565 | + if (err == 0 && cnt <= TB_RECURSIVE_CUTOFF) { | ||
| 564 | 566 | err = tb_displayline(f, | |
| 565 | 567 | tb->tb_frame->f_code->co_filename, | |
| 566 | 568 | tb->tb_lineno, | |
@@ -571,7 +573,7 @@ tb_printinternal(PyTracebackObject *tb, PyObject *f, long limit) | |||
| 571 | 573 | } | |
| 572 | 574 | tb = tb->tb_next; | |
| 573 | 575 | } | |
| 574 | - if (err == 0 && cnt > 3) { | ||
| 576 | + if (err == 0 && cnt > TB_RECURSIVE_CUTOFF) { | ||
| 575 | 577 | err = tb_print_line_repeated(f, cnt); | |
| 576 | 578 | } | |
| 577 | 579 | return err; | |
| Back | FazBrowse Home | New Git URL |
0 commit comments