Skip to content

Make Notebook.backup() crash-safe - #343

Open
Flerpharos wants to merge 1 commit into
mainfrom
fix/342-backup-crash-safety
Open

Make Notebook.backup() crash-safe#343
Flerpharos wants to merge 1 commit into
mainfrom
fix/342-backup-crash-safety

Conversation

@Flerpharos

Copy link
Copy Markdown
Collaborator

What

Fixes #342 (MED — data loss + resource leak; from the Codex audit). Two defects in Notebook.backup():

  1. Stream leak on mkdir failure — the HTTP stream was opened, then path.parent.mkdir(...) ran before with stream:. A mkdir failure (permissions, or a parent path component that is a file) skipped the with block, leaking the response/connection until GC.
  2. Interrupted download destroyed the existing archive — the destination was opened with "wb", truncating any existing file immediately; a mid-download failure left the previous good backup gone and a corrupt partial in its place.

Fix

  • Create the destination directory inside with stream:, so the stream is always closed even if mkdir fails.
  • Download to a NamedTemporaryFile in the destination directory and atomically replace() it over the destination only after the download completes. On any failure (including KeyboardInterrupt) the partial temp file is removed and the existing archive is left untouched.

Verification

New tests: test_backup_interrupted_preserves_existing_archive (mid-stream failure → existing archive intact, no .part left) and test_backup_closes_stream_when_mkdir_fails (mkdir failure → response.close() called). Full suite 432 passed, 1 skipped; ruff/pyright clean.

Fixes #342.

🤖 Generated with Claude Code

Two defects in backup():
- The HTTP stream was opened before `path.parent.mkdir(...)`, which ran
  outside `with stream`; a mkdir failure (permissions, or a parent that is a
  file) leaked the response/connection until GC.
- The destination was opened with "wb", truncating any existing archive
  immediately; a mid-download failure destroyed the previous good backup and
  left a corrupt partial at the destination.

Create the directory inside `with stream:` (so the stream is always closed),
and download to a NamedTemporaryFile in the destination directory, atomically
`replace()`-ing it over the destination only after the download completes. On
any failure the partial temp file is removed and the existing archive is left
untouched.

Fixes #342.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Notebook.backup() is not crash-safe: leaks the stream on mkdir failure and destroys an existing archive on interrupted download

1 participant