Skip to content

Commit d106b30

Browse files
committed
fix python repl interaction
1 parent c04bd64 commit d106b30

5 files changed

Lines changed: 48 additions & 12 deletions

File tree

README.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,15 @@ I like to work with [Chez Scheme](https://cisco.github.io/ChezScheme/). Suppose
4242
```yaml
4343
#| file: test/scheme.yml
4444
#| file: test/scheme.yml
45+
#| file: test/scheme.yml
46+
#| file: test/scheme.yml
47+
#| file: test/scheme.yml
4548
config:
4649
command: "scheme --eedisable"
4750
first_prompt: "> "
4851
change_prompt: '(waiter-prompt-string "{key}>")'
4952
prompt: "{key}> "
50-
continuation_prompt: ""
53+
# continuation_prompt: ""
5154
commands:
5255
- command: (* 6 7)
5356
- command: |
@@ -77,6 +80,9 @@ This looks very similar to the previous example:
7780
```yaml
7881
#| file: test/lua.yml
7982
#| file: test/lua.yml
83+
#| file: test/lua.yml
84+
#| file: test/lua.yml
85+
#| file: test/lua.yml
8086
config:
8187
command: "lua"
8288
first_prompt: "> "
@@ -119,6 +125,9 @@ The Python REPL got a revision in version 3.13, with lots of colour and ANSI cod
119125
```yaml
120126
#| file: test/python.yml
121127
#| file: test/python.yml
128+
#| file: test/python.yml
129+
#| file: test/python.yml
130+
#| file: test/python.yml
122131
config:
123132
command: python -q
124133
first_prompt: ">>>"
@@ -127,6 +136,7 @@ config:
127136
continuation_prompt: "{key}\\+\\+\\+ "
128137
environment:
129138
NO_COLOR: "1"
139+
PYTHON_BASIC_REPL: "1"
130140
commands:
131141
- command: print("Hello, World!")
132142
- command: 6 * 7
@@ -145,6 +155,9 @@ The user can configure how the REPL is called and interpreted.
145155
```python
146156
#| id: input-data
147157
#| id: input-data
158+
#| id: input-data
159+
#| id: input-data
160+
#| id: input-data
148161
class ReplConfig(msgspec.Struct):
149162
"""Configuration
150163
@@ -178,6 +191,9 @@ Then, a session is a list of commands. Each command should be a UTF-8 string, an
178191
```python
179192
#| id: input-data
180193
#| id: input-data
194+
#| id: input-data
195+
#| id: input-data
196+
#| id: input-data
181197
class ReplCommand(msgspec.Struct):
182198
"""A command to be sent to the REPL.
183199

docs/index.md

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,17 @@ The core of the implementation is handled by the `pexpect` library. We have a sm
77
```python
88
#| id: repl-contextmanager
99
#| id: repl-contextmanager
10+
#| id: repl-contextmanager
11+
#| id: repl-contextmanager
12+
#| id: repl-contextmanager
1013
def spawn(config: ReplConfig):
14+
env = dict(os.environ) | config.environment
1115
child: pexpect.spawn[str] = pexpect.spawn(
1216
config.command,
1317
timeout=config.timeout,
1418
echo=False,
1519
encoding="utf-8",
16-
env=config.environment,
20+
env=dict(os.environ) | config.environment,
1721
)
1822
return child
1923

@@ -47,7 +51,7 @@ def repl(config: ReplConfig) -> Generator[Callable[[str], str | None]]:
4751

4852
still_waiting: bool = True
4953
for line in lines:
50-
logging.debug("sending: %s", line)
54+
logging.debug("sending: '%s'", line)
5155
_ = child.sendline(line)
5256
logging.debug("waiting for prompt or continuation")
5357
_ = child.expect(
@@ -66,7 +70,7 @@ def repl(config: ReplConfig) -> Generator[Callable[[str], str | None]]:
6670

6771
if still_waiting:
6872
logging.debug(f"waiting for last prompt")
69-
# _ = child.sendline("")
73+
_ = child.sendline("")
7074
_ = child.expect(prompt)
7175
logging.debug(f"got: %s", child.before)
7276
if child.before:
@@ -76,7 +80,7 @@ def repl(config: ReplConfig) -> Generator[Callable[[str], str | None]]:
7680
return None
7781

7882
if config.strip_ansi:
79-
ansi_escape = re.compile(r"(\u001b\[|\x1B\[)[0-?]*[ -\/]*[@-~]")
83+
ansi_escape = re.compile(r"(\u001b|\x1B)(\[[0-?]*[ -\/]*[@-~]|[\>\=])")
8084
return ansi_escape.sub("", answer[-1].strip())
8185

8286
return answer[-1].strip()
@@ -94,7 +98,7 @@ def repl(config: ReplConfig) -> Generator[Callable[[str], str | None]]:
9498
return None
9599

96100
if config.strip_ansi:
97-
ansi_escape = re.compile(r"(\u001b\[|\x1B\[)[0-?]*[ -\/]*[@-~]")
101+
ansi_escape = re.compile(r"(\u001b|\x1B)(\[[0-?]*[ -\/]*[@-~]|[\>\=])")
98102
return ansi_escape.sub("", answer)
99103

100104
return answer
@@ -109,6 +113,9 @@ We use this to run a session. The session is modified in place.
109113
```python
110114
#| id: run-session
111115
#| id: run-session
116+
#| id: run-session
117+
#| id: run-session
118+
#| id: run-session
112119
def run_session(session: ReplSession):
113120
with repl(session.config) as run:
114121
for cmd in session.commands:
@@ -129,6 +136,9 @@ I/O is handled by `msgspec`.
129136
```python
130137
#| id: io
131138
#| id: io
139+
#| id: io
140+
#| id: io
141+
#| id: io
132142
def read_session(port: IO[str] = sys.stdin) -> ReplSession:
133143
data: str = port.read()
134144
return msgspec.yaml.decode(data, type=ReplSession)
@@ -146,6 +156,9 @@ def write_session(session: ReplSession, port: IO[str] = sys.stdout):
146156
```python
147157
#| id: imports
148158
#| id: imports
159+
#| id: imports
160+
#| id: imports
161+
#| id: imports
149162
# from datetime import datetime, tzinfo
150163
from typing import IO, cast
151164
from collections.abc import Generator, Callable
@@ -156,6 +169,7 @@ import uuid
156169
import sys
157170
import re
158171
import logging
172+
import os
159173

160174
import pexpect
161175
import msgspec
@@ -171,6 +185,9 @@ __version__ = importlib.metadata.version("repl-session")
171185
```python
172186
#| file: src/repl_session/__init__.py
173187
#| file: src/repl_session/__init__.py
188+
#| file: src/repl_session/__init__.py
189+
#| file: src/repl_session/__init__.py
190+
#| file: src/repl_session/__init__.py
174191
"""
175192
`repl-session` is a command-line tool to evaluate a given session
176193
in any REPL, and store the results.

src/repl_session/__init__.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import sys
1818
import re
1919
import logging
20+
import os
2021

2122
import pexpect
2223
import msgspec
@@ -93,12 +94,13 @@ class ReplSession(msgspec.Struct):
9394
# ~/~ begin <<docs/index.md#repl-contextmanager>>[init]
9495
#| id: repl-contextmanager
9596
def spawn(config: ReplConfig):
97+
env = dict(os.environ) | config.environment
9698
child: pexpect.spawn[str] = pexpect.spawn(
9799
config.command,
98100
timeout=config.timeout,
99101
echo=False,
100102
encoding="utf-8",
101-
env=config.environment,
103+
env=dict(os.environ) | config.environment,
102104
)
103105
return child
104106

@@ -132,7 +134,7 @@ def send(msg: str) -> str | None:
132134

133135
still_waiting: bool = True
134136
for line in lines:
135-
logging.debug("sending: %s", line)
137+
logging.debug("sending: '%s'", line)
136138
_ = child.sendline(line)
137139
logging.debug("waiting for prompt or continuation")
138140
_ = child.expect(
@@ -151,7 +153,7 @@ def send(msg: str) -> str | None:
151153

152154
if still_waiting:
153155
logging.debug(f"waiting for last prompt")
154-
# _ = child.sendline("")
156+
_ = child.sendline("")
155157
_ = child.expect(prompt)
156158
logging.debug(f"got: %s", child.before)
157159
if child.before:
@@ -161,7 +163,7 @@ def send(msg: str) -> str | None:
161163
return None
162164

163165
if config.strip_ansi:
164-
ansi_escape = re.compile(r"(\u001b\[|\x1B\[)[0-?]*[ -\/]*[@-~]")
166+
ansi_escape = re.compile(r"(\u001b|\x1B)(\[[0-?]*[ -\/]*[@-~]|[\>\=])")
165167
return ansi_escape.sub("", answer[-1].strip())
166168

167169
return answer[-1].strip()
@@ -179,7 +181,7 @@ def send(msg: str) -> str | None:
179181
return None
180182

181183
if config.strip_ansi:
182-
ansi_escape = re.compile(r"(\u001b\[|\x1B\[)[0-?]*[ -\/]*[@-~]")
184+
ansi_escape = re.compile(r"(\u001b|\x1B)(\[[0-?]*[ -\/]*[@-~]|[\>\=])")
183185
return ansi_escape.sub("", answer)
184186

185187
return answer

test/python.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ config:
88
continuation_prompt: "{key}\\+\\+\\+ "
99
environment:
1010
NO_COLOR: "1"
11+
PYTHON_BASIC_REPL: "1"
1112
commands:
1213
- command: print("Hello, World!")
1314
- command: 6 * 7

test/scheme.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ config:
55
first_prompt: "> "
66
change_prompt: '(waiter-prompt-string "{key}>")'
77
prompt: "{key}> "
8-
continuation_prompt: ""
8+
# continuation_prompt: ""
99
commands:
1010
- command: (* 6 7)
1111
- command: |

0 commit comments

Comments
 (0)