Repository navigation
Windows CI、per-fd 定位 IO 锁与沙箱门面保真 - #11
Merged
Merged
Conversation
added 5 commits
September 30, 2026 01:45
…t job (#7) PR #5 and the compat layer were validated on a real Windows machine exactly once, by hand. Every platform primitive the package touches lives in pyvdisk/compat.py, and the emulation tests force those paths on Linux -- but only a real Windows runner exercises msvcrt.locking, the directory-fsync no-op and drive-letter handling against the actual OS. GitHub-hosted windows-latest runners are free for public repos, so: - no-dependencies gains an os matrix (ubuntu-latest, windows-latest): the zero-dependency promise, the full stdlib-only suite and a quickstart smoke now hold on both platforms; - Lint gains errors-windows: pylint -E resolves imports against the running interpreter, so an unguarded `import fcntl` -- the exact bug that made PR #5 necessary -- is an import-error on Windows, not a runtime surprise. The hnswlib tests job stays Linux-only: the accelerator has no Windows wheels and building it on the runner adds flake for no extra coverage of compat.
…ss (#8) PR #5's disk.py locked emulated positional IO per fd; the compat layer collapsed that into a single module-level RLock, which on Windows serialises block IO across *every* open DataDisk -- disk A's writes queue disk B's. The docstring's 'not a global lock' claim did not survive the refactor it was written for. Restore the per-descriptor design from the PR: _PIO_LOCKS maps fd -> RLock, created on demand behind a tiny guard. Entries are not pruned -- a reused fd number shares its predecessor's lock, which can only add contention, never incorrectness, and pruning would need a close hook this layer does not own. Two tests pin the behaviour: same fd reuses one lock and different fds never share one, and IO on descriptor b completes while descriptor a's lock is held -- the exact scenario the global lock froze.
_SandboxFS wrapped move/copy/link/symlink/readlink/truncate/chmod/chown/ utime, but the capability-scoped view underneath implements none of them: the wrappers could only ever die with a bare AttributeError. The VScript stdlib advertises the full disk's fs API, so an agent script that reached for one of these verbs got an error that read like a sandbox bug, giving the model nothing to correct with. Drop the dead wrappers and make __getattr__ refuse anything the scoped view does not implement with the carried-verb list instead -- which also keeps future scoped verbs from leaking into the sandbox door unreviewed. Path-less diagnostics (df, fsck, statfs) still pass straight through, and stat() now forwards follow=, which stdlib fs_chmod/fs_chown rely on. The new test drives the refusal through run_script for symlink/chmod/ truncate and asserts the message names the verbs that do exist.
The new errors-windows job earned its keep on the first push: on Windows, pylint's os brain has no pread/pwrite/getuid/getgid to infer from, so the guarded POSIX branches in compat (and the uid/gid assertions) read as E1101. Disable no-member on exactly those four lines, each with a comment naming the guard -- a bare module-level disable would also hide real no-member bugs on os elsewhere.
Found by the new no-dependencies (windows-latest) job, first real-Windows run of the sandbox suite: str(tmp_path) carries backslashes, and the VScript lexer correctly rejects '\U' inside a plain string -- so the test failed with a LexError before it ever reached the disk. Same rule PR #5 taught the host tests; the policy layer takes the mixed separators fine. On Linux as_posix() is a no-op, which is why CI never saw it. Windows suite now stands at 445 passed / 6 skipped, one failure gone.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
三件事,各自独立成提交,分别关闭一个跟进 issue:
no-dependenciesjob 加os矩阵(ubuntu-latest + windows-latest),零依赖承诺、stdlib-only 测试套件与新增的 quickstart 冒烟从此在两个平台同时成立;Lint 工作流新增
errors-windows——pylint-E 在真实 Windows 解释器上解析导入,未加守卫的
import fcntl(正是催生 PR fix(windows): 让镜像层、宿主锁与 CLI 在 Windows 上可用 #5 的那类 bug)会直接变成 import-error。hnswlib 测试 job 保持 Linux-only:加速器无 Windows wheel,为它维护编译只添 flake。_PIO_LOCKS按 fd 建锁(PR fix(windows): 让镜像层、宿主锁与 CLI 在 Windows 上可用 #5 的原设计),模块级单锁在 Windows 上让所有盘互相排队的问题消除;条目不修剪(fd 复用只会多抢锁,不影响正确性)。两个回归测试:同 fd 同锁/异 fd 异锁,以及"A 锁持有中 B 照常完成 IO"。move/copy/link/symlink/readlink/truncate/chmod/chown/utime这 9 个死包装(scoped 层根本没有对应方法,只会裸 AttributeError);__getattr__改为用带动词清单的SandboxError拒绝,也防止未来 scoped 新动词绕过审查漏进沙箱门。顺手修了stat()不透传follow=(stdlib fs_chmod/fs_chown 依赖它)。验证:本地 452 passed(449 + 2 个 per-fd 测试 + 1 个沙箱拒绝测试)、
pylint -E0 error。Closes #7, closes #8, closes #9。