repl-session 0.1.0__tar.gz → 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: repl-session
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Runs a session with a Read-Eval-Print-Loop (REPL). Takes YAML as input gives JSON as output.
5
5
  Classifier: Development Status :: 3 - Alpha
6
6
  Classifier: Environment :: Console
@@ -60,19 +60,19 @@ I like to work with [Chez Scheme](https://cisco.github.io/ChezScheme/). Suppose
60
60
  ```yaml
61
61
  #| file: test/scheme.yml
62
62
  config:
63
- command: "scheme --eedisable"
64
- first_prompt: "> "
65
- change_prompt: "(waiter-prompt-string \"{key}>\")"
66
- next_prompt: "{key}> "
67
- strip_command: true
63
+ command: "scheme --eedisable"
64
+ first_prompt: "> "
65
+ change_prompt: '(waiter-prompt-string "{key}>")'
66
+ prompt: "{key}> "
67
+ continuation_prompt: ""
68
68
  commands:
69
- - command: (* 6 7)
70
- - command: |
69
+ - command: (* 6 7)
70
+ - command: |
71
71
  (define (fac n init)
72
72
  (if (zero? n)
73
73
  init
74
74
  (fac (- n 1) (* init n)))))
75
- - command: (fac 10 1)
75
+ - command: (fac 10 1)
76
76
  ```
77
77
 
78
78
  Passing this to `repl-session`, it will start the Chez Scheme interpreter, waiting for the `>` prompt to appear. It then changes the prompt to a generated `uuid4` code, for instance `27e87a8a-742c-4501-b05d-b05814f5a010> `. This will make sure that we can't accidentally match something else for an interactive prompt (imagine we're generating some XML!). Since commands are also echoed to standard out, we need to strip them from the resulting output. Running this should give:
@@ -94,15 +94,27 @@ This looks very similar to the previous example:
94
94
  ```yaml
95
95
  #| file: test/lua.yml
96
96
  config:
97
- command: "lua"
98
- first_prompt: "> "
99
- change_prompt: "_PROMPT = \"{key}> \""
100
- next_prompt: "{key}> "
101
- strip_command: true
102
- strip_ansi: true
97
+ command: "lua"
98
+ first_prompt: "> "
99
+ change_prompt: '_PROMPT = "{key}> "; _PROMPT2 = "{key}+ "'
100
+ prompt: "{key}> "
101
+ continuation_prompt: "{key}\\+ "
102
+ strip_ansi: true
103
103
  commands:
104
- - command: 6 * 7
105
- - command: "\"Hello\" .. \", \" .. \"World!\""
104
+ - command: 6 * 7
105
+ - command: '"Hello" .. ", " .. "World!"'
106
+ - command: |
107
+ function fac(n, m)
108
+ if m == nil then
109
+ return fac(n, 1)
110
+ end
111
+ if n == 0 then
112
+ return m
113
+ else
114
+ return fac(n-1, m*n)
115
+ end
116
+ end
117
+ - command: fac(10)
106
118
  ```
107
119
 
108
120
  The Lua REPL is not so nice. It sends ANSI escape codes and those need to be filtered out.
@@ -116,6 +128,31 @@ repl-session < test/lua.yml | jq '.commands.[].output'
116
128
  "Hello, World!"
117
129
  ```
118
130
 
131
+ ### Python
132
+
133
+ The Python REPL got a revision in version 3.13, with lots of colour and ANSI codes.
134
+
135
+ ```yaml
136
+ #| file: test/python.yml
137
+ config:
138
+ command: python -q
139
+ first_prompt: ">>>"
140
+ change_prompt: 'import sys; sys.ps1 = "{key}>>> "; sys.ps2 = "{key}+++ "'
141
+ prompt: "{key}>>> "
142
+ continuation_prompt: "{key}\\+\\+\\+ "
143
+ environment:
144
+ NO_COLOR: "1"
145
+ commands:
146
+ - command: print("Hello, World!")
147
+ - command: 6 * 7
148
+ - command: |
149
+ def fac(n):
150
+ for i in range(1, n):
151
+ n *= i
152
+ return n
153
+ - command: fac(10)
154
+ ```
155
+
119
156
  ## Input/Output structure
120
157
 
121
158
  The user can configure how the REPL is called and interpreted.
@@ -137,14 +174,17 @@ class ReplConfig(msgspec.Struct):
137
174
  output; useful if the REPL echoes your input before answering.
138
175
  timeout (float): Command timeout for this session in seconds.
139
176
  """
177
+
140
178
  command: str
141
179
  first_prompt: str
142
180
  change_prompt: str
143
- next_prompt: str
144
- append_newline: bool = True
145
- strip_command: bool = False
181
+ prompt: str
182
+ continuation_prompt: str | None = None
146
183
  strip_ansi: bool = False
184
+ environment: dict[str, str] = msgspec.field(default_factory=dict)
147
185
  timeout: float = 5.0
186
+
187
+
148
188
  ```
149
189
 
150
190
  Then, a session is a list of commands. Each command should be a UTF-8 string, and we allow to attach some meta-data like expected MIME type for the output. We can also pass an expected output in the case of a documentation test. If `output` was already given on the input, it is moved to `expected`. This way it becomes really easy to setup regression tests on your documentation. Just rerun on the generated output file.
@@ -160,6 +200,7 @@ class ReplCommand(msgspec.Struct):
160
200
  output (str | None): evaluated output.
161
201
  expected (str | None): expected output.
162
202
  """
203
+
163
204
  command: str
164
205
  output_type: str = "text/plain"
165
206
  output: str | None = None
@@ -173,8 +214,11 @@ class ReplSession(msgspec.Struct):
173
214
  config (ReplConfig): Config for setting up a REPL session.
174
215
  commands (list[ReplCommand]): List of commands in the session.
175
216
  """
217
+
176
218
  config: ReplConfig
177
219
  commands: list[ReplCommand]
220
+
221
+
178
222
  ```
179
223
 
180
224
  ## License and contribution
@@ -42,19 +42,19 @@ I like to work with [Chez Scheme](https://cisco.github.io/ChezScheme/). Suppose
42
42
  ```yaml
43
43
  #| file: test/scheme.yml
44
44
  config:
45
- command: "scheme --eedisable"
46
- first_prompt: "> "
47
- change_prompt: "(waiter-prompt-string \"{key}>\")"
48
- next_prompt: "{key}> "
49
- strip_command: true
45
+ command: "scheme --eedisable"
46
+ first_prompt: "> "
47
+ change_prompt: '(waiter-prompt-string "{key}>")'
48
+ prompt: "{key}> "
49
+ continuation_prompt: ""
50
50
  commands:
51
- - command: (* 6 7)
52
- - command: |
51
+ - command: (* 6 7)
52
+ - command: |
53
53
  (define (fac n init)
54
54
  (if (zero? n)
55
55
  init
56
56
  (fac (- n 1) (* init n)))))
57
- - command: (fac 10 1)
57
+ - command: (fac 10 1)
58
58
  ```
59
59
 
60
60
  Passing this to `repl-session`, it will start the Chez Scheme interpreter, waiting for the `>` prompt to appear. It then changes the prompt to a generated `uuid4` code, for instance `27e87a8a-742c-4501-b05d-b05814f5a010> `. This will make sure that we can't accidentally match something else for an interactive prompt (imagine we're generating some XML!). Since commands are also echoed to standard out, we need to strip them from the resulting output. Running this should give:
@@ -76,15 +76,27 @@ This looks very similar to the previous example:
76
76
  ```yaml
77
77
  #| file: test/lua.yml
78
78
  config:
79
- command: "lua"
80
- first_prompt: "> "
81
- change_prompt: "_PROMPT = \"{key}> \""
82
- next_prompt: "{key}> "
83
- strip_command: true
84
- strip_ansi: true
79
+ command: "lua"
80
+ first_prompt: "> "
81
+ change_prompt: '_PROMPT = "{key}> "; _PROMPT2 = "{key}+ "'
82
+ prompt: "{key}> "
83
+ continuation_prompt: "{key}\\+ "
84
+ strip_ansi: true
85
85
  commands:
86
- - command: 6 * 7
87
- - command: "\"Hello\" .. \", \" .. \"World!\""
86
+ - command: 6 * 7
87
+ - command: '"Hello" .. ", " .. "World!"'
88
+ - command: |
89
+ function fac(n, m)
90
+ if m == nil then
91
+ return fac(n, 1)
92
+ end
93
+ if n == 0 then
94
+ return m
95
+ else
96
+ return fac(n-1, m*n)
97
+ end
98
+ end
99
+ - command: fac(10)
88
100
  ```
89
101
 
90
102
  The Lua REPL is not so nice. It sends ANSI escape codes and those need to be filtered out.
@@ -98,6 +110,31 @@ repl-session < test/lua.yml | jq '.commands.[].output'
98
110
  "Hello, World!"
99
111
  ```
100
112
 
113
+ ### Python
114
+
115
+ The Python REPL got a revision in version 3.13, with lots of colour and ANSI codes.
116
+
117
+ ```yaml
118
+ #| file: test/python.yml
119
+ config:
120
+ command: python -q
121
+ first_prompt: ">>>"
122
+ change_prompt: 'import sys; sys.ps1 = "{key}>>> "; sys.ps2 = "{key}+++ "'
123
+ prompt: "{key}>>> "
124
+ continuation_prompt: "{key}\\+\\+\\+ "
125
+ environment:
126
+ NO_COLOR: "1"
127
+ commands:
128
+ - command: print("Hello, World!")
129
+ - command: 6 * 7
130
+ - command: |
131
+ def fac(n):
132
+ for i in range(1, n):
133
+ n *= i
134
+ return n
135
+ - command: fac(10)
136
+ ```
137
+
101
138
  ## Input/Output structure
102
139
 
103
140
  The user can configure how the REPL is called and interpreted.
@@ -119,14 +156,17 @@ class ReplConfig(msgspec.Struct):
119
156
  output; useful if the REPL echoes your input before answering.
120
157
  timeout (float): Command timeout for this session in seconds.
121
158
  """
159
+
122
160
  command: str
123
161
  first_prompt: str
124
162
  change_prompt: str
125
- next_prompt: str
126
- append_newline: bool = True
127
- strip_command: bool = False
163
+ prompt: str
164
+ continuation_prompt: str | None = None
128
165
  strip_ansi: bool = False
166
+ environment: dict[str, str] = msgspec.field(default_factory=dict)
129
167
  timeout: float = 5.0
168
+
169
+
130
170
  ```
131
171
 
132
172
  Then, a session is a list of commands. Each command should be a UTF-8 string, and we allow to attach some meta-data like expected MIME type for the output. We can also pass an expected output in the case of a documentation test. If `output` was already given on the input, it is moved to `expected`. This way it becomes really easy to setup regression tests on your documentation. Just rerun on the generated output file.
@@ -142,6 +182,7 @@ class ReplCommand(msgspec.Struct):
142
182
  output (str | None): evaluated output.
143
183
  expected (str | None): expected output.
144
184
  """
185
+
145
186
  command: str
146
187
  output_type: str = "text/plain"
147
188
  output: str | None = None
@@ -155,8 +196,11 @@ class ReplSession(msgspec.Struct):
155
196
  config (ReplConfig): Config for setting up a REPL session.
156
197
  commands (list[ReplCommand]): List of commands in the session.
157
198
  """
199
+
158
200
  config: ReplConfig
159
201
  commands: list[ReplCommand]
202
+
203
+
160
204
  ```
161
205
 
162
206
  ## License and contribution
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "repl-session"
3
- version = "0.1.0"
3
+ version = "0.2.0"
4
4
  description = "Runs a session with a Read-Eval-Print-Loop (REPL). Takes YAML as input gives JSON as output."
5
5
  readme = "README.md"
6
6
  classifiers = [
@@ -24,6 +24,8 @@ dev = [
24
24
  "entangled-cli>=2.1.13",
25
25
  "mkdocs-macros-plugin>=1.3.9",
26
26
  "mkdocs-material>=9.6.20",
27
+ "python-lsp-server>=1.13.1",
28
+ "ruff>=0.13.2",
27
29
  "ty>=0.0.1a21",
28
30
  ]
29
31
 
@@ -4,16 +4,19 @@
4
4
  `repl-session` is a command-line tool to evaluate a given session
5
5
  in any REPL, and store the results.
6
6
  """
7
+
7
8
  # ~/~ begin <<docs/index.md#imports>>[init]
8
9
  #| id: imports
9
10
  # from datetime import datetime, tzinfo
10
- from typing import IO
11
+ from typing import IO, cast
11
12
  from collections.abc import Generator, Callable
13
+
12
14
  # import re
13
15
  from contextlib import contextmanager
14
16
  import uuid
15
17
  import sys
16
18
  import re
19
+ import logging
17
20
 
18
21
  import pexpect
19
22
  import msgspec
@@ -24,6 +27,7 @@ import importlib.metadata
24
27
  __version__ = importlib.metadata.version("repl-session")
25
28
  # ~/~ end
26
29
 
30
+
27
31
  # ~/~ begin <<README.md#input-data>>[init]
28
32
  #| id: input-data
29
33
  class ReplConfig(msgspec.Struct):
@@ -41,14 +45,17 @@ class ReplConfig(msgspec.Struct):
41
45
  output; useful if the REPL echoes your input before answering.
42
46
  timeout (float): Command timeout for this session in seconds.
43
47
  """
48
+
44
49
  command: str
45
50
  first_prompt: str
46
51
  change_prompt: str
47
- next_prompt: str
48
- append_newline: bool = True
49
- strip_command: bool = False
52
+ prompt: str
53
+ continuation_prompt: str | None = None
50
54
  strip_ansi: bool = False
55
+ environment: dict[str, str] = msgspec.field(default_factory=dict)
51
56
  timeout: float = 5.0
57
+
58
+
52
59
  # ~/~ end
53
60
  # ~/~ begin <<README.md#input-data>>[1]
54
61
  #| id: input-data
@@ -61,6 +68,7 @@ class ReplCommand(msgspec.Struct):
61
68
  output (str | None): evaluated output.
62
69
  expected (str | None): expected output.
63
70
  """
71
+
64
72
  command: str
65
73
  output_type: str = "text/plain"
66
74
  output: str | None = None
@@ -74,45 +82,95 @@ class ReplSession(msgspec.Struct):
74
82
  config (ReplConfig): Config for setting up a REPL session.
75
83
  commands (list[ReplCommand]): List of commands in the session.
76
84
  """
85
+
77
86
  config: ReplConfig
78
87
  commands: list[ReplCommand]
88
+
89
+
79
90
  # ~/~ end
80
91
 
92
+
81
93
  # ~/~ begin <<docs/index.md#repl-contextmanager>>[init]
82
94
  #| id: repl-contextmanager
95
+ def spawn(config: ReplConfig):
96
+ child: pexpect.spawn[str] = pexpect.spawn(
97
+ config.command,
98
+ timeout=config.timeout,
99
+ echo=False,
100
+ encoding="utf-8",
101
+ env=config.environment,
102
+ )
103
+ return child
104
+
83
105
 
84
106
  @contextmanager
85
- def repl(config: ReplConfig) -> Generator[Callable[[str], str]]:
107
+ def repl(config: ReplConfig) -> Generator[Callable[[str], str | None]]:
86
108
  key = str(uuid.uuid4())
109
+ change_prompt_cmd = config.change_prompt.format(key=key)
110
+ prompt = config.prompt.format(key=key)
111
+ continuation_prompt = (
112
+ config.continuation_prompt.format(key=key)
113
+ if config.continuation_prompt is not None
114
+ else None
115
+ )
116
+
117
+ child: pexpect.spawn[str]
118
+ with spawn(config) as child:
119
+ _ = child.expect(config.first_prompt)
120
+ _ = child.sendline(change_prompt_cmd)
121
+ # if config.strip_command:
122
+ # child.expect(key)
123
+ _ = child.expect(prompt)
124
+
125
+ def send(msg: str) -> str | None:
126
+ nonlocal prompt, continuation_prompt, change_prompt_cmd
127
+ lines = msg.splitlines()
128
+ answer: list[str] = []
129
+
130
+ still_waiting: bool = True
131
+ for line in lines:
132
+ logging.debug("sending: %s", line)
133
+ _ = child.sendline(line)
134
+ if continuation_prompt is not None:
135
+ logging.debug("waiting for prompt or continuation")
136
+ _ = child.expect(
137
+ f"(?P<cont>{continuation_prompt})|(?P<norm>{prompt})"
138
+ )
139
+ if not isinstance(child.match, re.Match):
140
+ continue
141
+ if child.match.group("cont") is not None:
142
+ logging.debug("continuation")
143
+ still_waiting = True
144
+ else:
145
+ logging.debug("done: %s", child.before)
146
+ if child.before is not None:
147
+ answer.append(child.before)
148
+ still_waiting = False
149
+ else:
150
+ logging.debug("waiting for prompt")
151
+ _ = child.expect(prompt)
152
+ if child.before:
153
+ answer.append(child.before)
154
+ still_waiting = False
155
+
156
+ if still_waiting:
157
+ _ = child.sendline("")
158
+ _ = child.expect(prompt)
159
+ if child.before:
160
+ answer.append(child.before)
161
+
162
+ if not answer:
163
+ return None
87
164
 
88
- with pexpect.spawn(config.command, timeout=config.timeout) as child:
89
- child.expect(config.first_prompt)
90
- change_prompt_cmd = config.change_prompt.format(key=key)
91
- if config.append_newline:
92
- change_prompt_cmd = change_prompt_cmd + "\n"
93
- child.send(change_prompt_cmd)
94
- if config.strip_command:
95
- child.expect(key)
96
- prompt = config.next_prompt.format(key=key)
97
- child.expect(prompt)
98
-
99
- def send(msg: str) -> str:
100
- if config.append_newline:
101
- msg = msg + "\n"
102
- child.send(msg)
103
- child.expect("(.*)" + prompt)
104
-
105
- answer = child.match[1].decode()
106
165
  if config.strip_ansi:
107
- ansi_escape = re.compile(r'(\u001b\[|\x1B\[)[0-?]*[ -\/]*[@-~]')
108
- answer = ansi_escape.sub("", answer)
109
- if config.strip_command:
110
- answer = answer.strip().replace("\r", "")
111
- return answer.removeprefix(msg)
112
- else:
113
- return child.match[1].decode()
166
+ ansi_escape = re.compile(r"(\u001b\[|\x1B\[)[0-?]*[ -\/]*[@-~]")
167
+ return ansi_escape.sub("", answer[-1].strip())
168
+
169
+ return answer[-1].strip()
114
170
 
115
171
  yield send
172
+
173
+
116
174
  # ~/~ end
117
175
  # ~/~ begin <<docs/index.md#run-session>>[init]
118
176
  #| id: run-session
@@ -125,10 +183,11 @@ def run_session(session: ReplSession):
125
183
  cmd.expected = expected
126
184
 
127
185
  return session
186
+
187
+
128
188
  # ~/~ end
129
189
  # ~/~ begin <<docs/index.md#io>>[init]
130
190
  #| id: io
131
-
132
191
  def read_session(port: IO[str] = sys.stdin) -> ReplSession:
133
192
  data: str = port.read()
134
193
  return msgspec.yaml.decode(data, type=ReplSession)
@@ -136,12 +195,15 @@ def read_session(port: IO[str] = sys.stdin) -> ReplSession:
136
195
 
137
196
  def write_session(session: ReplSession, port: IO[str] = sys.stdout):
138
197
  data = msgspec.json.encode(session)
139
- port.write(data.decode())
198
+ _ = port.write(data.decode())
199
+
200
+
140
201
  # ~/~ end
141
202
 
142
203
 
143
204
  @argh.arg("-v", "--version", help="show version and exit")
144
- def repl_session(version: bool = False):
205
+ @argh.arg("-l", "--log-enable", help="show debugging output")
206
+ def repl_session(version: bool = False, log_enable: bool = False):
145
207
  """
146
208
  repl-session runs a REPL session, reading JSON from standard input and
147
209
  writing to standard output. Both the input and output follow the same
@@ -151,9 +213,14 @@ def repl_session(version: bool = False):
151
213
  print(f"repl-session {__version__}")
152
214
  sys.exit(0)
153
215
 
216
+ if log_enable:
217
+ logging.basicConfig(level=logging.DEBUG)
218
+
154
219
  write_session(run_session(read_session()))
155
220
 
156
221
 
157
222
  def main():
158
223
  argh.dispatch_command(repl_session)
224
+
225
+
159
226
  # ~/~ end
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: repl-session
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Runs a session with a Read-Eval-Print-Loop (REPL). Takes YAML as input gives JSON as output.
5
5
  Classifier: Development Status :: 3 - Alpha
6
6
  Classifier: Environment :: Console
@@ -60,19 +60,19 @@ I like to work with [Chez Scheme](https://cisco.github.io/ChezScheme/). Suppose
60
60
  ```yaml
61
61
  #| file: test/scheme.yml
62
62
  config:
63
- command: "scheme --eedisable"
64
- first_prompt: "> "
65
- change_prompt: "(waiter-prompt-string \"{key}>\")"
66
- next_prompt: "{key}> "
67
- strip_command: true
63
+ command: "scheme --eedisable"
64
+ first_prompt: "> "
65
+ change_prompt: '(waiter-prompt-string "{key}>")'
66
+ prompt: "{key}> "
67
+ continuation_prompt: ""
68
68
  commands:
69
- - command: (* 6 7)
70
- - command: |
69
+ - command: (* 6 7)
70
+ - command: |
71
71
  (define (fac n init)
72
72
  (if (zero? n)
73
73
  init
74
74
  (fac (- n 1) (* init n)))))
75
- - command: (fac 10 1)
75
+ - command: (fac 10 1)
76
76
  ```
77
77
 
78
78
  Passing this to `repl-session`, it will start the Chez Scheme interpreter, waiting for the `>` prompt to appear. It then changes the prompt to a generated `uuid4` code, for instance `27e87a8a-742c-4501-b05d-b05814f5a010> `. This will make sure that we can't accidentally match something else for an interactive prompt (imagine we're generating some XML!). Since commands are also echoed to standard out, we need to strip them from the resulting output. Running this should give:
@@ -94,15 +94,27 @@ This looks very similar to the previous example:
94
94
  ```yaml
95
95
  #| file: test/lua.yml
96
96
  config:
97
- command: "lua"
98
- first_prompt: "> "
99
- change_prompt: "_PROMPT = \"{key}> \""
100
- next_prompt: "{key}> "
101
- strip_command: true
102
- strip_ansi: true
97
+ command: "lua"
98
+ first_prompt: "> "
99
+ change_prompt: '_PROMPT = "{key}> "; _PROMPT2 = "{key}+ "'
100
+ prompt: "{key}> "
101
+ continuation_prompt: "{key}\\+ "
102
+ strip_ansi: true
103
103
  commands:
104
- - command: 6 * 7
105
- - command: "\"Hello\" .. \", \" .. \"World!\""
104
+ - command: 6 * 7
105
+ - command: '"Hello" .. ", " .. "World!"'
106
+ - command: |
107
+ function fac(n, m)
108
+ if m == nil then
109
+ return fac(n, 1)
110
+ end
111
+ if n == 0 then
112
+ return m
113
+ else
114
+ return fac(n-1, m*n)
115
+ end
116
+ end
117
+ - command: fac(10)
106
118
  ```
107
119
 
108
120
  The Lua REPL is not so nice. It sends ANSI escape codes and those need to be filtered out.
@@ -116,6 +128,31 @@ repl-session < test/lua.yml | jq '.commands.[].output'
116
128
  "Hello, World!"
117
129
  ```
118
130
 
131
+ ### Python
132
+
133
+ The Python REPL got a revision in version 3.13, with lots of colour and ANSI codes.
134
+
135
+ ```yaml
136
+ #| file: test/python.yml
137
+ config:
138
+ command: python -q
139
+ first_prompt: ">>>"
140
+ change_prompt: 'import sys; sys.ps1 = "{key}>>> "; sys.ps2 = "{key}+++ "'
141
+ prompt: "{key}>>> "
142
+ continuation_prompt: "{key}\\+\\+\\+ "
143
+ environment:
144
+ NO_COLOR: "1"
145
+ commands:
146
+ - command: print("Hello, World!")
147
+ - command: 6 * 7
148
+ - command: |
149
+ def fac(n):
150
+ for i in range(1, n):
151
+ n *= i
152
+ return n
153
+ - command: fac(10)
154
+ ```
155
+
119
156
  ## Input/Output structure
120
157
 
121
158
  The user can configure how the REPL is called and interpreted.
@@ -137,14 +174,17 @@ class ReplConfig(msgspec.Struct):
137
174
  output; useful if the REPL echoes your input before answering.
138
175
  timeout (float): Command timeout for this session in seconds.
139
176
  """
177
+
140
178
  command: str
141
179
  first_prompt: str
142
180
  change_prompt: str
143
- next_prompt: str
144
- append_newline: bool = True
145
- strip_command: bool = False
181
+ prompt: str
182
+ continuation_prompt: str | None = None
146
183
  strip_ansi: bool = False
184
+ environment: dict[str, str] = msgspec.field(default_factory=dict)
147
185
  timeout: float = 5.0
186
+
187
+
148
188
  ```
149
189
 
150
190
  Then, a session is a list of commands. Each command should be a UTF-8 string, and we allow to attach some meta-data like expected MIME type for the output. We can also pass an expected output in the case of a documentation test. If `output` was already given on the input, it is moved to `expected`. This way it becomes really easy to setup regression tests on your documentation. Just rerun on the generated output file.
@@ -160,6 +200,7 @@ class ReplCommand(msgspec.Struct):
160
200
  output (str | None): evaluated output.
161
201
  expected (str | None): expected output.
162
202
  """
203
+
163
204
  command: str
164
205
  output_type: str = "text/plain"
165
206
  output: str | None = None
@@ -173,8 +214,11 @@ class ReplSession(msgspec.Struct):
173
214
  config (ReplConfig): Config for setting up a REPL session.
174
215
  commands (list[ReplCommand]): List of commands in the session.
175
216
  """
217
+
176
218
  config: ReplConfig
177
219
  commands: list[ReplCommand]
220
+
221
+
178
222
  ```
179
223
 
180
224
  ## License and contribution
File without changes
File without changes