|
91 | 91 | import tempfile |
92 | 92 | import textwrap |
93 | 93 | import tokenize |
| 94 | +import functools |
94 | 95 | import itertools |
95 | 96 | import traceback |
96 | 97 | import linecache |
@@ -348,6 +349,37 @@ def get_default_backend(): |
348 | 349 | return _default_backend |
349 | 350 |
|
350 | 351 |
|
| 352 | +class PdbPyReplInput: |
| 353 | + def __init__(self, pdb_instance, prompt): |
| 354 | + from _pyrepl.readline import _setup |
| 355 | + self.pdb_instance = pdb_instance |
| 356 | + self.prompt = prompt |
| 357 | + self.console = code.InteractiveConsole() |
| 358 | + _setup({}) |
| 359 | + |
| 360 | + def readline(self): |
| 361 | + from _pyrepl.simple_interact import _more_lines |
| 362 | + from _pyrepl.readline import get_completer, multiline_input, set_completer |
| 363 | + |
| 364 | + def more_lines(text): |
| 365 | + cmd, _, line = self.pdb_instance.parseline(text) |
| 366 | + if not line or not cmd: |
| 367 | + return False |
| 368 | + func = getattr(self.pdb_instance, 'do_' + cmd, None) |
| 369 | + if func is not None: |
| 370 | + return False |
| 371 | + return _more_lines(self.console, text) |
| 372 | + |
| 373 | + try: |
| 374 | + pyrepl_completer = get_completer() |
| 375 | + set_completer(self.pdb_instance.complete) |
| 376 | + return multiline_input(more_lines, self.prompt, '... ') + '\n' |
| 377 | + except EOFError: |
| 378 | + return 'EOF' |
| 379 | + finally: |
| 380 | + set_completer(pyrepl_completer) |
| 381 | + |
| 382 | + |
351 | 383 | class Pdb(bdb.Bdb, cmd.Cmd): |
352 | 384 | _previous_sigint_handler = None |
353 | 385 |
|
@@ -382,6 +414,10 @@ def __init__(self, completekey='tab', stdin=None, stdout=None, skip=None, |
382 | 414 | except ImportError: |
383 | 415 | pass |
384 | 416 |
|
| 417 | + if self.use_rawinput and stdin is None: |
| 418 | + self.pyrepl_input = PdbPyReplInput(self, self.prompt) |
| 419 | + else: |
| 420 | + self.pyrepl_input = None |
385 | 421 | self.allow_kbdint = False |
386 | 422 | self.nosigint = nosigint |
387 | 423 | # Consider these characters as part of the command so when the users type |
@@ -620,14 +656,40 @@ def user_exception(self, frame, exc_info): |
620 | 656 | self.message('%s%s' % (prefix, self._format_exc(exc_value))) |
621 | 657 | self.interaction(frame, exc_traceback) |
622 | 658 |
|
| 659 | + @contextmanager |
| 660 | + def _replace_attribute(self, attrs): |
| 661 | + original_attrs = {} |
| 662 | + for attr, value in attrs.items(): |
| 663 | + original_attrs[attr] = getattr(self, attr) |
| 664 | + setattr(self, attr, value) |
| 665 | + try: |
| 666 | + yield |
| 667 | + finally: |
| 668 | + for attr, value in original_attrs.items(): |
| 669 | + setattr(self, attr, value) |
| 670 | + |
| 671 | + @contextmanager |
| 672 | + def _maybe_use_pyrepl_as_stdin(self): |
| 673 | + if self.pyrepl_input is None: |
| 674 | + yield |
| 675 | + return |
| 676 | + |
| 677 | + with self._replace_attribute({ |
| 678 | + 'stdin': self.pyrepl_input, |
| 679 | + 'use_rawinput': False, |
| 680 | + 'prompt': '', |
| 681 | + }): |
| 682 | + yield |
| 683 | + |
623 | 684 | # General interaction function |
624 | 685 | def _cmdloop(self): |
625 | 686 | while True: |
626 | 687 | try: |
627 | 688 | # keyboard interrupts allow for an easy way to cancel |
628 | 689 | # the current command, so allow them during interactive input |
629 | 690 | self.allow_kbdint = True |
630 | | - self.cmdloop() |
| 691 | + with self._maybe_use_pyrepl_as_stdin(): |
| 692 | + self.cmdloop() |
631 | 693 | self.allow_kbdint = False |
632 | 694 | break |
633 | 695 | except KeyboardInterrupt: |
|
0 commit comments