11from __future__ import unicode_literals
22
33import contextlib
4+ import errno
45import functools
56import os
67import os .path
78import shutil
9+ import stat
810import subprocess
911import tarfile
1012import tempfile
1113
1214import pkg_resources
1315
16+ from pre_commit import five
17+
1418
1519@contextlib .contextmanager
1620def cwd (path ):
@@ -46,7 +50,7 @@ def clean_path_on_failure(path):
4650 yield
4751 except BaseException :
4852 if os .path .exists (path ):
49- shutil . rmtree (path )
53+ rmtree (path )
5054 raise
5155
5256
@@ -78,7 +82,7 @@ def tmpdir():
7882 try :
7983 yield tempdir
8084 finally :
81- shutil . rmtree (tempdir )
85+ rmtree (tempdir )
8286
8387
8488def resource_filename (filename ):
@@ -135,6 +139,13 @@ def cmd_output(*cmd, **kwargs):
135139 if stdin is not None :
136140 stdin = stdin .encode ('UTF-8' )
137141
142+ # py2/py3 on windows are more strict about the types here
143+ cmd = [five .n (arg ) for arg in cmd ]
144+ kwargs ['env' ] = dict (
145+ (five .n (key ), five .n (value ))
146+ for key , value in kwargs .pop ('env' , {}).items ()
147+ ) or None
148+
138149 popen_kwargs .update (kwargs )
139150 proc = __popen (cmd , ** popen_kwargs )
140151 stdout , stderr = proc .communicate (stdin )
@@ -150,3 +161,15 @@ def cmd_output(*cmd, **kwargs):
150161 )
151162
152163 return proc .returncode , stdout , stderr
164+
165+
166+ def rmtree (path ):
167+ """On windows, rmtree fails for readonly dirs."""
168+ def handle_remove_readonly (func , path , exc ): # pragma: no cover (windows)
169+ excvalue = exc [1 ]
170+ if func in (os .rmdir , os .remove ) and excvalue .errno == errno .EACCES :
171+ os .chmod (path , stat .S_IRWXU | stat .S_IRWXG | stat .S_IRWXO )
172+ func (path )
173+ else :
174+ raise
175+ shutil .rmtree (path , ignore_errors = False , onerror = handle_remove_readonly )
0 commit comments