Documentation
The document/docstring for shutil.move states:
If the destination is on the current filesystem, then :func:
os.renameis used.
Otherwise, src is copied to dst using copy_function and then removed.
which seems to be incorrect. The actual behavior shows below:
- If
srcandtargetare on the same filesystem,os.renameis preferred, which is an atomic operation. - But, if
os.renamefails due to some reason (e.g. permission denied), thenshutil.movewill silently fallback to nonatomiccopy+remove.
This fallback behavior is crucial in case atomic operation matters. It creates false statement that an operation must be atomic, while in fact is non-atomic in some cases.
This behavior can be found in source code here.
For example
Given all following dirs and files on the SAME filesystem
// 600: rw_ ___ ___
// 700: rwx ___ ___
userA:groupA 700 /dirSrc
userA:groupA 600 /dirSrc/fileSrc
userB:groupB 700 /dirTarget
userA:groupA 600 /dirTarget/fileTarget
When the userA calls:
# Run by userA shutil.move('/dirSrc/fileSrc', '/dirTarget/fileTarget') # Move succeeded
Based on current doc, user expects that the move must be atomic since both files are on the same filesystem. But in fact, this operation will fall back to copy+del silently, which is not atomic.
Linked PRs
- gh-109503: Fix document for
shutil.moveon usage ofos.renamesince it's inaccurate #109504 - gh-109503: Fix document for
shutil.moveon usage ofos.renamesince it's inaccurate #109507 - [3.15] gh-109503: Fix document for shutil.move() on usage of os.rename() since it's inaccurate (GH-109507) #150611
- [3.14] gh-109503: Fix document for shutil.move() on usage of os.rename() since it's inaccurate (GH-109507) #150612
- [3.13] gh-109503: Fix document for shutil.move() on usage of os.rename() since it's inaccurate (GH-109507) #150613