|
17 | 17 | console_locals = globals().copy()
|
18 | 18 |
|
19 | 19 | import code
|
| 20 | +import readline |
| 21 | +import rlcompleter |
20 | 22 | import sys
|
21 | 23 |
|
| 24 | +class DynamicCompleter(rlcompleter.Completer): |
| 25 | + """ |
| 26 | + A custom completer that dynamically updates its namespace to include new |
| 27 | + imports made within the interactive session. |
| 28 | + """ |
| 29 | + def __init__(self, namespace): |
| 30 | + # Store a reference to the namespace, not a copy, so that changes to the namespace are |
| 31 | + # reflected. |
| 32 | + self.namespace = namespace |
| 33 | + |
| 34 | + def complete(self, text, state): |
| 35 | + # Update the completer's internal namespace with the current interactive session's locals |
| 36 | + # and globals. This is the key to making new imports discoverable. |
| 37 | + rlcompleter.Completer.__init__(self, self.namespace) |
| 38 | + return super().complete(text, state) |
| 39 | + |
| 40 | + |
22 | 41 | if sys.stdin.isatty():
|
23 | 42 | # Use the default options.
|
24 | 43 | exitmsg = None
|
|
28 | 47 | sys.ps1 = ""
|
29 | 48 | sys.ps2 = ""
|
30 | 49 |
|
| 50 | +# Set up tab completion. |
| 51 | +completer = DynamicCompleter(console_locals) |
| 52 | +readline.set_completer(completer.complete) |
| 53 | + |
| 54 | +# TODO(jpwoodbu): Use readline.backend instead of readline.__doc__ once we can depend on having |
| 55 | +# Python >=3.13. |
| 56 | +if 'libedit' in readline.__doc__: # type: ignore |
| 57 | + readline.parse_and_bind("bind ^I rl_complete") |
| 58 | +elif 'GNU readline' in readline.__doc__: # type: ignore |
| 59 | + readline.parse_and_bind("tab: complete") |
| 60 | +else: |
| 61 | + print('Could not enable tab completion!') |
| 62 | + |
31 | 63 | # We set the banner to an empty string because the repl_template.py file already prints the banner.
|
32 | 64 | code.interact(local=console_locals, banner="", exitmsg=exitmsg)
|
0 commit comments