fix(os): keep the process patch off while a backend serves an op

A disk mount whose root sits at or under its own virtual prefix hands
the host a path is_mounted answers True for, so the patched os module
routed the backend's own physical path back into the same backend and
the process wedged instead of raising. ops/host_io.py is the bypass:
the two patched doors read it, and the two places a backend actually
runs (execute_op, execute_cmd) plus the watch delta walk set it.
Streams are wrapped, since a backend opens its file on the first
__anext__. It is a process-global depth rather than a ContextVar
because aiofiles reaches the host through loop.run_in_executor, which
drops the context.

os.readlink off a mount hands back the host's answer untouched, so a
bytes path answers bytes rather than a str of them.

One truth file now runs twice, the second time with -X utf8=0, the
mode where pathlib passes io.open's "locale" sentinel.
This commit is contained in:
Zecheng Zhang
2026-08-20 21:04:08 -07:00
parent 5639b62790
commit d1eea898b8
11 changed files with 345 additions and 13 deletions
+1
View File
@@ -33,6 +33,7 @@
"ops": {
"python_only": {
"file": "MirageFile: a sync file object over the Ops facade for embedding python code (with open-style usage). An npm consumer holds promises, not file objects, so there is nothing to mirror.",
"host_io": "The re-entrancy bypass the two patched doors (ops/open, ops/os_patch) read, so a backend serving an op reaches the host instead of being handed its own physical path back. Nothing to mirror for the same reason os_patch has nothing: node is never monkeypatched.",
"open": "The open() constructor for MirageFile; same embedding convenience as ops/file.",
"os_patch": "Patches the host interpreter's builtins.open/os functions onto a workspace, a CPython-only trick with no npm analogue."
}