Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ END_UNRELEASED_TEMPLATE

{#v0-0-0-added}
### Added
* (repl) Default stub now has tab completion, where `readline` support is available,
see ([#3114](https://github.com/bazel-contrib/rules_python/pull/3114)).
([#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)