Windows single-file mailbox rewrite can remove the mailbox when the fallback rename fails
還沒有人認領這個 Issue。
評估
研究方向
從 mailbox 模組中的 _singlefileMailbox.flush() 開始,檢查 mailbox.mbox、mailbox.MMDF 和 mailbox.Babyl 的替換路徑。重現 issue 中提到的兩個模擬 os.rename 失敗,然後新增或更新回歸測試,以確保替換失敗時原始 mailbox 路徑名稱仍然保留;開始前先查看關聯的 PR gh-156700。
由索引模型根據 Issue 內容生成。
描述
Bug report
Bug description
Summary
On Windows, _singlefileMailbox.flush() cannot rename a temporary rewrite over an existing mailbox with os.rename(). Its FileExistsError fallback deletes the original mailbox and then performs a second rename. If that second filesystem operation fails, the mailbox pathname has already been removed.
The affected mailbox classes are mailbox.mbox, mailbox.MMDF, and mailbox.Babyl. The temporary replacement normally remains, so the contents are not necessarily irretrievable, but the original mailbox pathname is missing. This is a Windows-specific failure path because it depends on Windows os.rename() refusing to replace an existing destination.
Reproduction Code
import errno
import glob
import mailbox
import os
import tempfile
from unittest.mock import patch
with tempfile.TemporaryDirectory() as directory:
path = os.path.join(directory, "mailbox")
box = mailbox.mbox(path)
first = box.add(b"Subject: first\n\nfirst\n")
box.add(b"Subject: second\n\nsecond\n")
box.flush()
box.remove(first)
# Simulate Windows rejecting the first replacement attempt, followed by
# an I/O failure while the fallback performs its second rename.
with patch(
"mailbox.os.rename",
side_effect=[
FileExistsError(errno.EEXIST, "target exists"),
OSError(errno.EIO, "injected second rename failure"),
],
):
try:
box.flush()
except OSError as error:
print("flush:", type(error).__name__, error.errno)
print("mailbox exists:", os.path.exists(path))
print(
"temporary files:",
[os.path.basename(name) for name in glob.glob(path + ".*")],
)
Observed on a Windows CPython build:
flush: OSError 5
mailbox exists: False
temporary files: ['mailbox.1788174401.yuu.26684']
Actual Behavior
After the first os.rename() raises FileExistsError, flush() removes the existing mailbox pathname. If the second os.rename() then fails, the exception propagates after the original mailbox has already been removed.
The rewritten temporary file normally remains, but the mailbox is no longer available at its original pathname.
Expected Behavior
If replacement of the rewritten mailbox fails, the original mailbox pathname should remain intact. Replacing an existing mailbox should not require explicitly removing the destination before installing the rewritten file.
CPython versions tested on
CPython main branch
Operating systems tested on
Windows
Linked PRs
- gh-156700
- 主要語言
- Python
- 星號
- 77.2k
- 分支
- 36k
- 平均合併
- 1 天 9 小時
- 30 天內合併 PR
- 558
貢獻指南
從這裡開始
- 先讀完整個 Issue,再讀專案的貢獻指南。
- 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
- Fork 儲存庫,在一個分支上完成修改。
- 送出 Pull Request,並在描述裡引用這個 Issue 編號。
python/cpython 的其他 Issue
-
docs pending
難度 2/5 1-3 小時 新手友好度 78/100
-
stdlib type-feature
難度 2/5 1-3 小時 新手友好度 78/100
-
stdlib type-feature
難度 2/5 1-3 小時 新手友好度 72/100
-
build type-bug
難度 2/5 1-3 小時 新手友好度 76/100
-
stdlib topic-email type-feature
難度 2/5 1-3 小時 新手友好度 70/100
相似的 Issue
-
難度 2/5 1-3 小時 新手友好度 74/100
bancolombia/sentinel#23 ·
-
test md 未關閉CI
難度 2/5 1-3 小時 新手友好度 74/100
-
integration:quickjs org:external priority:backlog topic:code-interpreter topic:middleware type:feature
難度 2/5 1-3 小時 新手友好度 74/100
langchain-ai/deepagents#6450 ·
-
bug client
難度 2/5 1-3 小時 新手友好度 88/100
-
難度 2/5 1-3 小時 新手友好度 74/100