Skip to content

Commit e3dbb91

Browse files
committed
fix: tab completion for REPL
fixes #3090
1 parent 5281261 commit e3dbb91

File tree

2 files changed

+35
-1
lines changed

2 files changed

+35
-1
lines changed

python/bin/repl_stub.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,27 @@
1717
console_locals = globals().copy()
1818

1919
import code
20+
import readline
21+
import rlcompleter
2022
import sys
2123

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+
2241
if sys.stdin.isatty():
2342
# Use the default options.
2443
exitmsg = None
@@ -28,5 +47,18 @@
2847
sys.ps1 = ""
2948
sys.ps2 = ""
3049

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+
3163
# We set the banner to an empty string because the repl_template.py file already prints the banner.
3264
code.interact(local=console_locals, banner="", exitmsg=exitmsg)

python/private/repl.bzl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
"""Implementation of the rules to expose a REPL."""
22

33
load("//python:py_binary.bzl", _py_binary = "py_binary")
4+
load("@bazel_skylib//lib:paths.bzl", "paths")
45

56
def _generate_repl_main_impl(ctx):
67
stub_repo = ctx.attr.stub.label.repo_name or ctx.workspace_name
7-
stub_path = "/".join([stub_repo, ctx.file.stub.short_path])
8+
unnormalized_path = "/".join([stub_repo, ctx.file.stub.short_path])
9+
stub_path = paths.normalize(unnormalized_path)
810

911
out = ctx.actions.declare_file(ctx.label.name + ".py")
1012

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy