UPDATE: Underlying python bug has been fixed by cpython#14064, which will be part of python 3.13 (mitigation code for earlier versions below).
You are probably on Mac OSX and your directory is at least partially on a non-Mac filesystem (ie not HFS+). On those, Mac filesystem drivers automatically create binary companion files prefixed with ._
to record so-called extended attributes (explained in https://apple.stackexchange.com/questions/14980/why-are-dot-underscore-files-created-and-how-can-i-avoid-them, but also illustrated below).
rmtree
on systems which do not support file descriptors in os.scandir
(like Mac OSX) now unsafely creates a list of entries and then unlinks them one by one (creating a known race-condition: https://github.com/python/cpython/blob/908fd691f96403a3c30d85c17dd74ed1f26a60fd/Lib/shutil.py#L592-L621). Unfortunately two separate behaviours make this condition true every time:
- the original file is always listed before the extended attributes one, and
- when the original file is unlinked (
test.txt
) the meta file (._test.txt
) is removed simultaneously.
Thus, the extended attribute file will be missing when it is its turn and throw the FileNotFoundError
you are experiencing.
I think this bug would be best addressed by cpython#14064, which aims at ignoring FileNotFoundError
s in rmtree
generally.
Mitigation
In the mean time you could ignore unlinking errors on those meta files with onerror
:
def ignore_extended_attributes(func, filename, exc_info):
is_meta_file = os.path.basename(filename).startswith("._")
if not (func is os.unlink and is_meta_file):
raise
shutil.rmtree(path_dir, onerror=ignore_extended_attributes)
Show case of Mac's extended attributes
To illustrate you can create a small ExFAT disk image and mount it to /Volumes/Untitled
with the commands
hdiutil create -size 5m -fs exfat test.dmg
hdiutil attach test.dmg # mounts at /Volumes/Untitled
cd /Volumes/Untitled
mkdir test # create a directory to remove
cd test
touch test.txt
open test.txt # open the test.txt file in the standard editor
Just opening the file in the standard text editor creates an extended attributes file ._test.txt
and records the last access time in it:
/Volumes/Untitled/test $ ls -a
. .. ._test.txt test.txt
/Volumes/Untitled/test $ xattr test.txt
com.apple.lastuseddate#PS
The problem is that unlinking the original file automatically also unlinks the companion file.
/Volumes/Untitled/test $ rm test.txt
/Volumes/Untitled/test $ ls -a
. ..
path_dir
a path to a symbolic link? – Maximamaximal