Skip to content

Commit c04bd64

Browse files
committed
fix for when command is empty
1 parent 444c061 commit c04bd64

3 files changed

Lines changed: 53 additions & 24 deletions

File tree

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ I like to work with [Chez Scheme](https://cisco.github.io/ChezScheme/). Suppose
4141

4242
```yaml
4343
#| file: test/scheme.yml
44+
#| file: test/scheme.yml
4445
config:
4546
command: "scheme --eedisable"
4647
first_prompt: "> "
@@ -75,6 +76,7 @@ This looks very similar to the previous example:
7576
7677
```yaml
7778
#| file: test/lua.yml
79+
#| file: test/lua.yml
7880
config:
7981
command: "lua"
8082
first_prompt: "> "
@@ -116,6 +118,7 @@ The Python REPL got a revision in version 3.13, with lots of colour and ANSI cod
116118

117119
```yaml
118120
#| file: test/python.yml
121+
#| file: test/python.yml
119122
config:
120123
command: python -q
121124
first_prompt: ">>>"
@@ -141,6 +144,7 @@ The user can configure how the REPL is called and interpreted.
141144
142145
```python
143146
#| id: input-data
147+
#| id: input-data
144148
class ReplConfig(msgspec.Struct):
145149
"""Configuration
146150
@@ -173,6 +177,7 @@ Then, a session is a list of commands. Each command should be a UTF-8 string, an
173177

174178
```python
175179
#| id: input-data
180+
#| id: input-data
176181
class ReplCommand(msgspec.Struct):
177182
"""A command to be sent to the REPL.
178183

docs/index.md

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ The core of the implementation is handled by the `pexpect` library. We have a sm
66

77
```python
88
#| id: repl-contextmanager
9+
#| id: repl-contextmanager
910
def spawn(config: ReplConfig):
1011
child: pexpect.spawn[str] = pexpect.spawn(
1112
config.command,
@@ -36,51 +37,67 @@ def repl(config: ReplConfig) -> Generator[Callable[[str], str | None]]:
3637
# child.expect(key)
3738
_ = child.expect(prompt)
3839

39-
def send(msg: str) -> str | None:
40-
nonlocal prompt, continuation_prompt, change_prompt_cmd
41-
lines = msg.splitlines()
42-
answer: list[str] = []
40+
if continuation_prompt is not None:
41+
def send(msg: str) -> str | None:
42+
lines = msg.splitlines()
43+
answer: list[str] = []
44+
45+
if not lines:
46+
return None
4347

44-
still_waiting: bool = True
45-
for line in lines:
46-
logging.debug("sending: %s", line)
47-
_ = child.sendline(line)
48-
if continuation_prompt is not None:
48+
still_waiting: bool = True
49+
for line in lines:
50+
logging.debug("sending: %s", line)
51+
_ = child.sendline(line)
4952
logging.debug("waiting for prompt or continuation")
5053
_ = child.expect(
51-
f"(?P<cont>{continuation_prompt})|(?P<norm>{prompt})"
54+
f"(?P<norm>{prompt})|(?P<cont>{continuation_prompt})"
5255
)
5356
if not isinstance(child.match, re.Match):
5457
continue
5558
if child.match.group("cont") is not None:
56-
logging.debug("continuation")
59+
logging.debug("continuation: %s -- %s", child.before, child.after)
5760
still_waiting = True
5861
else:
5962
logging.debug("done: %s", child.before)
6063
if child.before is not None:
6164
answer.append(child.before)
6265
still_waiting = False
63-
else:
64-
logging.debug("waiting for prompt")
66+
67+
if still_waiting:
68+
logging.debug(f"waiting for last prompt")
69+
# _ = child.sendline("")
6570
_ = child.expect(prompt)
71+
logging.debug(f"got: %s", child.before)
6672
if child.before:
6773
answer.append(child.before)
68-
still_waiting = False
6974

70-
if still_waiting:
71-
_ = child.sendline("")
75+
if not answer:
76+
return None
77+
78+
if config.strip_ansi:
79+
ansi_escape = re.compile(r"(\u001b\[|\x1B\[)[0-?]*[ -\/]*[@-~]")
80+
return ansi_escape.sub("", answer[-1].strip())
81+
82+
return answer[-1].strip()
83+
84+
else:
85+
def send(msg: str) -> str | None:
86+
logging.debug("sending: %s", msg)
87+
88+
_ = child.sendline(msg)
7289
_ = child.expect(prompt)
73-
if child.before:
74-
answer.append(child.before)
7590

76-
if not answer:
77-
return None
91+
answer = child.before.strip()
92+
93+
if not answer:
94+
return None
7895

79-
if config.strip_ansi:
80-
ansi_escape = re.compile(r"(\u001b\[|\x1B\[)[0-?]*[ -\/]*[@-~]")
81-
return ansi_escape.sub("", answer[-1].strip())
96+
if config.strip_ansi:
97+
ansi_escape = re.compile(r"(\u001b\[|\x1B\[)[0-?]*[ -\/]*[@-~]")
98+
return ansi_escape.sub("", answer)
8299

83-
return answer[-1].strip()
100+
return answer
84101

85102
yield send
86103

@@ -91,6 +108,7 @@ We use this to run a session. The session is modified in place.
91108

92109
```python
93110
#| id: run-session
111+
#| id: run-session
94112
def run_session(session: ReplSession):
95113
with repl(session.config) as run:
96114
for cmd in session.commands:
@@ -110,6 +128,7 @@ I/O is handled by `msgspec`.
110128

111129
```python
112130
#| id: io
131+
#| id: io
113132
def read_session(port: IO[str] = sys.stdin) -> ReplSession:
114133
data: str = port.read()
115134
return msgspec.yaml.decode(data, type=ReplSession)
@@ -126,6 +145,7 @@ def write_session(session: ReplSession, port: IO[str] = sys.stdout):
126145

127146
```python
128147
#| id: imports
148+
#| id: imports
129149
# from datetime import datetime, tzinfo
130150
from typing import IO, cast
131151
from collections.abc import Generator, Callable
@@ -150,6 +170,7 @@ __version__ = importlib.metadata.version("repl-session")
150170

151171
```python
152172
#| file: src/repl_session/__init__.py
173+
#| file: src/repl_session/__init__.py
153174
"""
154175
`repl-session` is a command-line tool to evaluate a given session
155176
in any REPL, and store the results.

src/repl_session/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ def send(msg: str) -> str | None:
127127
lines = msg.splitlines()
128128
answer: list[str] = []
129129

130+
if not lines:
131+
return None
132+
130133
still_waiting: bool = True
131134
for line in lines:
132135
logging.debug("sending: %s", line)

0 commit comments

Comments
 (0)