Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ END_UNRELEASED_TEMPLATE

{#v0-0-0-added}
### Added
* (repl) Default stub now has tab completion. ([#3114](https://github.com/bazel-contrib/rules_python/pull/3114)).
* (pypi) To configure the environment for `requirements.txt` evaluation, use the newly added
developer preview of the `pip.default` tag class. Only `rules_python` and root modules can use
this feature. You can also configure custom `config_settings` using `pip.default`.
Expand Down
44 changes: 44 additions & 0 deletions python/bin/repl_stub.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,28 @@
console_locals = globals().copy()

import code
import rlcompleter
import sys


class DynamicCompleter(rlcompleter.Completer):
"""
A custom completer that dynamically updates its namespace to include new
imports made within the interactive session.
"""

def __init__(self, namespace):
# Store a reference to the namespace, not a copy, so that changes to the namespace are
# reflected.
self.namespace = namespace

def complete(self, text, state):
# Update the completer's internal namespace with the current interactive session's locals
# and globals. This is the key to making new imports discoverable.
rlcompleter.Completer.__init__(self, self.namespace)
return super().complete(text, state)


if sys.stdin.isatty():
# Use the default options.
exitmsg = None
Expand All @@ -28,5 +48,29 @@
sys.ps1 = ""
sys.ps2 = ""

# Set up tab completion.
try:
import readline

completer = DynamicCompleter(console_locals)
readline.set_completer(completer.complete)

# TODO(jpwoodbu): Use readline.backend instead of readline.__doc__ once we can depend on having
# Python >=3.13.
if "libedit" in readline.__doc__: # type: ignore
readline.parse_and_bind("bind ^I rl_complete")
elif "GNU readline" in readline.__doc__: # type: ignore
readline.parse_and_bind("tab: complete")
else:
print(
"Could not enable tab completion: "
"unable to determine readline backend"
)
except ImportError:
print(
"Could not enable tab completion: "
"readline module not available on this platform"
)

# We set the banner to an empty string because the repl_template.py file already prints the banner.
code.interact(local=console_locals, banner="", exitmsg=exitmsg)