Skip to content

Commit 1226e41

Browse files
Technologicatclaude
andcommitted
Document that the REPL client reads piped stdin
The docs described only the interactive session. The client also runs each piped line in the session namespace, which is what lets a shell script drive a live app with nobody at the keyboard — reaching a state that would otherwise have to be clicked to, in a GUI app or anything else with a slow way in. With the trap that makes an automated check necessary, because it is invisible until it has already cost something: the session echoes the value of an expression and a statement has none, so a pipe made only of assignments prints nothing and is indistinguishable from one that never ran. Same banner, same prompts, same `Session closed.` A `print` shows up because `print` writes to stdout itself, which is what makes it usable as the check — and the demo app that `unpythonic.net.server` already runs as `__main__` is the throwaway host to try a pipe against beforehand. Found while driving a GUI app in another project through this client, where the first check piped an assignment and needed a purpose-built host to read the value back — work the demo app makes unnecessary. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hh7cgbLE8wSHudFVgTSzpi
1 parent 5208d45 commit 1226e41

3 files changed

Lines changed: 22 additions & 1 deletion

File tree

‎CHANGELOG.md‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
**2.4.1** (in progress):
44

5-
*No user-visible changes yet.*
5+
**Fixed**:
6+
7+
- The REPL documentation now says that the client reads piped stdin, running each line in the session namespace — so a shell script can drive a live app with nobody at the keyboard. The docs described only the interactive session, which is the smaller half of what the client does.
8+
- It also names the trap that makes an automated check necessary: the session echoes the *value* of an expression and a statement has none, so a pipe made only of assignments prints nothing and looks identical whether it ran or not. Have such a script `print` something and check for it; `python3 -m unpythonic.net.server` is a throwaway host to test the pipe against first.
69

710

811
---

‎README.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ This gives you a REPL, inside your live process, with all the power of Python. Y
135135

136136
You can have multiple REPL sessions connected simultaneously. When your app exits (for any reason), the server automatically shuts down, closing all connections if any remain. But exiting the client leaves the server running, so you can connect again later - that's the whole point.
137137

138+
The client reads piped stdin too, running each line in the session namespace, so a shell script can drive a live app with nobody at the keyboard. Note that the session echoes the value of an expression, and a statement has none - so a pipe made only of assignments looks the same whether it ran or not. See [the REPL docs](doc/repl.md) for how to check that it landed.
139+
138140
Optionally, if you have [mcpyrate](https://github.com/Technologicat/mcpyrate), the REPL sessions support importing, invoking and defining macros.
139141
</details>
140142
<details><summary>Industrial-strength scan and fold.</summary>

‎doc/repl.md‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,22 @@ This connects to the REPL server running on `localhost`, and opens a REPL sessio
9999
- Due to technical reasons, remote Ctrl+C currently only works on CPython. Support for PyPy3 would be nice, but currently not possible. See `unpythonic.misc.async_raise` and [#58](https://github.com/Technologicat/unpythonic/issues/58) for details.
100100
- Be sure to press `Ctrl+C` **just once**. Hammering the key combo may raise a `KeyboardInterrupt` locally in the code that is trying to send the remote `KeyboardInterrupt` (or in code waiting for the server's response), thus forcibly terminating the client. Starting immediately after the server has responded, remote Ctrl+C is available again. (The server indicates this by sending the text `KeyboardInterrupt`, possibly with a stack trace, and then giving a new prompt, just like a standard interactive Python session does.)
101101

102+
### Scripting the client
103+
104+
The client also reads piped stdin, running each line in the session namespace, so a shell script can drive a live app with nobody sitting at an interactive session:
105+
106+
```bash
107+
printf '%s\n' \
108+
'import mymodule' \
109+
'mymodule.retry_count = 5' \
110+
'print("retry_count is now", mymodule.retry_count)' \
111+
| python3 -m unpythonic.net.client 127.0.0.1
112+
```
113+
114+
- **Have the pipe print something, and check for it.** The session echoes the *value* of an expression, and a statement has none — so `mymodule.retry_count = 5` shows nothing at all, where `print(...)` is visible because `print` writes to stdout itself. A pipe made only of assignments therefore looks exactly the same whether it ran or not: same banner, same prompts, same `Session closed.` at the end. Anything automated that depends on the pipe having landed needs something to grep for, or it cannot tell a working session from one that silently did nothing.
115+
- `python3 -m unpythonic.net.server`, the demo app above, is a convenient throwaway host to test a pipe against before pointing it at a process that matters.
116+
- Consider wrapping the client in `timeout` where a script must not block.
117+
102118
### Netcat compatibility
103119

104120
If you don't need tab completion or `Ctrl+C` support, the main channel is also `netcat` compatible. Use `rlwrap` to get basic readline functionality (history):

0 commit comments

Comments
 (0)