Skip to content

Commit b91e8f8

Browse files
committed
REPL: use >>>> and .... as prompts to reduce chance of false positives
1 parent 0d75301 commit b91e8f8

File tree

2 files changed

+24
-7
lines changed

2 files changed

+24
-7
lines changed

unpythonic/net/client.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,8 +224,24 @@ def read_more_input():
224224
val = buf.getvalue()
225225
while True: # The "L" in the "REPL"
226226
try:
227-
# Wait for the prompt. It's the last thing the console
228-
# sends before listening for more input.
227+
# Wait for the prompt.
228+
#
229+
# We rely on the fact that it's always the last thing the console sends
230+
# before listening for more input.
231+
#
232+
# TODO: The current implementation leaves one race condition that can't be solved easily.
233+
# If the prompt string appears in the data stream, but is not actually a prompt,
234+
# we might interpret it as a prompt, and things will go south from there.
235+
#
236+
# This only happens when partial data recv'd on the socket ends exactly at the end of
237+
# the prompt string (e.g. ">>>"). So it's unlikely, but it may happen.
238+
#
239+
# The at-a-glance-almost-correct hack of prefixing bps1 and bps2 with b"\n" doesn't
240+
# work, because the newline won't be there when we handle the server's response to a
241+
# `KeyboardInterrupt` request. So doing that would cause the client to hang.
242+
#
243+
# As a semi-working hack, our server sets its prompts to ">>>>" and "...." (like PyPy).
244+
# Whereas "..." may appear in Python code or English, these strings usually don't.
229245
if val.endswith(bps1) or val.endswith(bps2):
230246
# "P"
231247
text = val.decode("utf-8")

unpythonic/net/server.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -436,11 +436,12 @@ def start(locals, addrspec=("127.0.0.1", 1337), banner=None):
436436
else:
437437
_banner = banner
438438

439-
# Set the prompts.
440-
if not hasattr(sys, "ps1"):
441-
sys.ps1 = ">>> "
442-
if not hasattr(sys, "ps2"):
443-
sys.ps2 = "... "
439+
# Set the prompts. We use four "." to make semi-sure the prompt string only appears as a prompt.
440+
# The client needs to identify the prompts from the data stream in order to know when to switch
441+
# between listening and prompting, so "..." is not even semi-safe (it's valid Python, as well as
442+
# valid English).
443+
sys.ps1 = ">>>> "
444+
sys.ps2 = ".... "
444445

445446
# We use a combo of Shim and ThreadLocalBox to redirect attribute lookups
446447
# to the thread-specific read/write streams.

0 commit comments

Comments
 (0)