44
55import os
66import shutil
7- import sys
87import time
98
109from . import _progress
@@ -48,30 +47,12 @@ def backoff_seconds(initial_interval, max_interval, max_total, factor=1.5):
4847
4948
5049def rmtree (path , timeout = 2.5 ):
51- # Try extra hard to clean up a temporary directory.
52-
53- if sys .version_info >= (3 , 12 ):
54-
55- def retry_unlink (func , path , exc ):
56- if func is os .unlink :
57- unlink_file (path , timeout = 0 )
58- else :
59- raise exc
60-
61- rmtree_kwargs = {"onexc" : retry_unlink }
62- else :
63-
64- def retry_unlink (func , path , exc_info ):
65- if func is os .unlink :
66- unlink_file (path , timeout = 0 )
67- else :
68- raise exc_info [1 ].with_traceback (exc_info [2 ])
69-
70- rmtree_kwargs = {"onerror" : retry_unlink }
50+ # Try extra hard to clean up a temporary directory. See comment in
51+ # unlink_file() for why.
7152
7253 for wait_seconds in backoff_seconds (0.001 , 0.5 , timeout ):
7354 try :
74- shutil .rmtree (path , ** rmtree_kwargs )
55+ shutil .rmtree (path )
7556 except OSError as e :
7657 if (
7758 hasattr (e , "winerror" )
@@ -86,51 +67,31 @@ def retry_unlink(func, path, exc_info):
8667
8768
8869def unlink_file (path , timeout = 2.5 ):
89- if sys .platform != "win32" :
90- return os .unlink (path )
91-
92- # On Windows, we sometimes encounter errors when trying to delete a file
93- # that we just closed after writing. This is due to Antivirus opening the
94- # file to scan it. Microsoft Defender Antivirus is said to use
95- # FILE_SHARE_DELETE, but os.unlink() calls DeleteFileW(), which does not
96- # use FILE_SHARE_DELETE; since the check is bidirectional, it fails.
97- # So use Win32 API calls that will not have this problem.
98-
99- import ctypes
100- from ctypes import wintypes
101-
102- kernel32 = ctypes .windll .kernel32
103- kernel32 .CreateFileW .restype = wintypes .HANDLE
104-
105- DELETE = 0x00010000
106- FILE_SHARE_READ = 0x01
107- FILE_SHARE_WRITE = 0x02
108- FILE_SHARE_DELETE = 0x04
109- OPEN_EXISTING = 3
110- FILE_FLAG_DELETE_ON_CLOSE = 0x04000000
111- INVALID_HANDLE_VALUE = wintypes .HANDLE (- 1 ).value
112-
113- def do_unlink (path ):
114- handle = INVALID_HANDLE_VALUE
115- try :
116- handle = kernel32 .CreateFileW (
117- str (path ),
118- DELETE ,
119- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE ,
120- None ,
121- OPEN_EXISTING ,
122- FILE_FLAG_DELETE_ON_CLOSE ,
123- None ,
124- )
125- if handle == INVALID_HANDLE_VALUE :
126- os .unlink (path ) # Let it raise an appropriate error.
127- finally :
128- if handle != INVALID_HANDLE_VALUE :
129- kernel32 .CloseHandle (handle )
70+ # On Windows, we may encounter errors when trying to delete a file that we
71+ # just closed after writing, due to Antivirus opening the file to scan it.
72+ # Microsoft Defender Antivirus is said to use FILE_SHARE_DELETE, but
73+ # os.unlink() calls DeleteFileW(), which does not use FILE_SHARE_DELETE;
74+ # since the check is bidirectional, it fails.
75+ #
76+ # (To be clear, this is probably rare and the exact conditions are unknown;
77+ # Also I have not actually observed it happening.)
78+ #
79+ # So we could use different Win32 API calls, namely CreateFile(path,
80+ # DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
81+ # OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, NULL) and then CloseHandle().
82+ # This should succeed on files being scanned.
83+ #
84+ # However, that method only marks the file for deleteion; actual deletion
85+ # happens once all processes close the file. This is a problem, because we
86+ # need to do the next thing, which is often to delete the parent directory
87+ # of the file. So we need to wait for the file to go away.
88+ #
89+ # And as long as we're waiting anyway, we can wait for regular deletion to
90+ # succeed, so we don't need to use Win32 API calls directly.
13091
13192 for wait_seconds in backoff_seconds (0.001 , 0.5 , timeout ):
13293 try :
133- do_unlink (path )
94+ os . unlink (path )
13495 except OSError as e :
13596 if (
13697 hasattr (e , "winerror" )
0 commit comments