Summary
When a file write is flushed during with-block exit or at interpreter teardown (an unclosed file), Pyex does not propagate an error returned by the filesystem backend's write_file/4. The error is silently swallowed, so a write that actually failed reports success — exit code 0, no exception — and the data is silently lost.
Only an explicit .close() currently surfaces the backend error.
Repro (with a backend whose write_file/4 can fail)
Using a Pyex.Filesystem backend where write_file/4 returns {:error, %VFS.Error{kind: :eio, message: "..."}} for an oversized value:
# 1. explicit close — error SURFACES (correct)
f = open("big.bin", "w")
f.write("x" * 6_000_000)
f.close() # -> raises IOError: ... value too large
# 2. with-block — error SWALLOWED (bug)
with open("big.bin", "w") as f:
f.write("x" * 6_000_000) # __exit__ closes, but the backend error is dropped
print("no exception") # <-- this prints
# 3. no close — error SWALLOWED (bug)
open("big.bin", "w").write("x" * 6_000_000)
print("no exception") # <-- this prints
- Expected: cases 2 and 3 raise the same
IOError as case 1 (the backend rejected the write).
- Actual: cases 2 and 3 complete with
exit_code 0 and no exception; the file is never written.
Why it matters
A Filesystem backend can legitimately reject a write (size limit, quota, permissions, I/O failure). Models overwhelmingly write with with open(...) as f: or a bare open(...).write(...) rather than an explicit .close(), so in practice the failure is invisible to the running program — it believes its data was saved when it was silently dropped.
Expected fix
with exit (__exit__) should propagate any error from the close/flush of the managed file.
- Flushing an unclosed file at end-of-run should at minimum surface the backend error (raise, or route it to stderr / a non-zero exit) rather than discarding it.
The backend's write_file/4 already returns the error; Pyex just needs to honor it on the flush paths, not only on explicit .close().
Summary
When a file write is flushed during
with-block exit or at interpreter teardown (an unclosed file), Pyex does not propagate an error returned by the filesystem backend'swrite_file/4. The error is silently swallowed, so a write that actually failed reports success — exit code 0, no exception — and the data is silently lost.Only an explicit
.close()currently surfaces the backend error.Repro (with a backend whose
write_file/4can fail)Using a
Pyex.Filesystembackend wherewrite_file/4returns{:error, %VFS.Error{kind: :eio, message: "..."}}for an oversized value:IOErroras case 1 (the backend rejected the write).exit_code 0and no exception; the file is never written.Why it matters
A
Filesystembackend can legitimately reject a write (size limit, quota, permissions, I/O failure). Models overwhelmingly write withwith open(...) as f:or a bareopen(...).write(...)rather than an explicit.close(), so in practice the failure is invisible to the running program — it believes its data was saved when it was silently dropped.Expected fix
withexit (__exit__) should propagate any error from the close/flush of the managed file.The backend's
write_file/4already returns the error; Pyex just needs to honor it on the flush paths, not only on explicit.close().