Skip to content

Commit 143ed94

Browse files
committed
Tests pass on windows
1 parent 56e5c4e commit 143ed94

File tree

21 files changed

+224
-109
lines changed

21 files changed

+224
-109
lines changed

pre_commit/commands/clean.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22
from __future__ import unicode_literals
33

44
import os.path
5-
import shutil
5+
6+
from pre_commit.util import rmtree
67

78

89
def clean(runner):
910
if os.path.exists(runner.store.directory):
10-
shutil.rmtree(runner.store.directory)
11+
rmtree(runner.store.directory)
1112
print('Cleaned {0}.'.format(runner.store.directory))
1213
return 0

pre_commit/five.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,17 @@
66

77
if PY2: # pragma: no cover (PY2 only)
88
text = unicode # flake8: noqa
9+
10+
def n(s):
11+
if isinstance(s, bytes):
12+
return s
13+
else:
14+
return s.encode('UTF-8')
915
else: # pragma: no cover (PY3 only)
1016
text = str
17+
18+
def n(s):
19+
if isinstance(s, text):
20+
return s
21+
else:
22+
return s.decode('UTF-8')

pre_commit/git.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
def get_root():
1818
path = os.getcwd()
19-
while len(path) > 1:
19+
while path != os.path.normpath(os.path.join(path, '../')):
2020
if os.path.exists(os.path.join(path, '.git')):
2121
return path
2222
else:

pre_commit/languages/node.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
class NodeEnv(helpers.Environment):
1414
@property
1515
def env_prefix(self):
16-
return '. {{prefix}}{0}/bin/activate &&'.format(ENVIRONMENT_DIR)
16+
return ". '{{prefix}}{0}/bin/activate' &&".format(ENVIRONMENT_DIR)
1717

1818

1919
@contextlib.contextmanager
@@ -37,7 +37,7 @@ def install_environment(repo_cmd_runner, version='default'):
3737
repo_cmd_runner.run(cmd)
3838

3939
with in_env(repo_cmd_runner) as node_env:
40-
node_env.run('cd {prefix} && npm install -g')
40+
node_env.run("cd '{prefix}' && npm install -g")
4141

4242

4343
def run_hook(repo_cmd_runner, hook, file_args):

pre_commit/languages/python.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
from __future__ import unicode_literals
22

33
import contextlib
4+
import distutils.spawn
5+
import os
6+
7+
import virtualenv
48

59
from pre_commit.languages import helpers
610
from pre_commit.util import clean_path_on_failure
@@ -12,25 +16,39 @@
1216
class PythonEnv(helpers.Environment):
1317
@property
1418
def env_prefix(self):
15-
return '. {{prefix}}{0}/bin/activate &&'.format(ENVIRONMENT_DIR)
19+
return ". '{{prefix}}{0}activate' &&".format(
20+
virtualenv.path_locations(
21+
ENVIRONMENT_DIR,
22+
)[-1].rstrip(os.sep) + os.sep,
23+
'activate',
24+
)
1625

1726

1827
@contextlib.contextmanager
1928
def in_env(repo_cmd_runner):
2029
yield PythonEnv(repo_cmd_runner)
2130

2231

32+
def norm_version(version):
33+
if os.name == 'nt': # pragma: no cover (windows)
34+
if not distutils.spawn.find_executable(version):
35+
# The default place for python on windows is:
36+
# C:\PythonXX\python.exe
37+
version = r'C:\{0}\python.exe'.format(version.replace('.', ''))
38+
return version
39+
40+
2341
def install_environment(repo_cmd_runner, version='default'):
2442
assert repo_cmd_runner.exists('setup.py')
2543

2644
# Install a virtualenv
2745
with clean_path_on_failure(repo_cmd_runner.path(ENVIRONMENT_DIR)):
2846
venv_cmd = ['virtualenv', '{{prefix}}{0}'.format(ENVIRONMENT_DIR)]
2947
if version != 'default':
30-
venv_cmd.extend(['-p', version])
48+
venv_cmd.extend(['-p', norm_version(version)])
3149
repo_cmd_runner.run(venv_cmd)
3250
with in_env(repo_cmd_runner) as env:
33-
env.run('cd {prefix} && pip install .')
51+
env.run("cd '{prefix}' && pip install .")
3452

3553

3654
def run_hook(repo_cmd_runner, hook, file_args):

pre_commit/logging_handler.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,4 @@ def emit(self, record):
3131
record.getMessage(),
3232
)
3333
)
34+
sys.stdout.flush()

pre_commit/make_archives.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
from __future__ import unicode_literals
44

55
import os.path
6-
import shutil
76

7+
from pre_commit import five
88
from pre_commit.util import cmd_output
99
from pre_commit.util import cwd
10+
from pre_commit.util import rmtree
1011
from pre_commit.util import tarfile_open
1112
from pre_commit.util import tmpdir
1213

@@ -50,11 +51,9 @@ def make_archive(name, repo, ref, destdir):
5051
# We don't want the '.git' directory
5152
# It adds a bunch of size to the archive and we don't use it at
5253
# runtime
53-
shutil.rmtree(os.path.join(tempdir, '.git'))
54+
rmtree(os.path.join(tempdir, '.git'))
5455

55-
# XXX: py2.6 derps if filename is unicode while writing
56-
# XXX: str() is used to preserve behavior in py3
57-
with tarfile_open(str(output_path), 'w|gz') as tf:
56+
with tarfile_open(five.n(output_path), 'w|gz') as tf:
5857
tf.add(tempdir, name)
5958

6059
return output_path

pre_commit/output.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@
88

99

1010
# TODO: smell: import side-effects
11-
COLS = int(
12-
subprocess.Popen(
13-
['tput', 'cols'], stdout=subprocess.PIPE,
14-
).communicate()[0] or
15-
# Default in the case of no terminal
16-
80
17-
)
11+
try:
12+
COLS = int(
13+
subprocess.Popen(
14+
['tput', 'cols'], stdout=subprocess.PIPE,
15+
).communicate()[0] or
16+
# Default in the case of no terminal
17+
80
18+
)
19+
except OSError: # pragma: no cover (windows)
20+
COLS = 80
1821

1922

2023
def get_hook_message(

pre_commit/store.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def _get_default_directory():
2828
"""
2929
return os.environ.get(
3030
'PRE_COMMIT_HOME',
31-
os.path.join(os.environ['HOME'], '.pre-commit'),
31+
os.path.join(os.path.expanduser('~'), '.pre-commit'),
3232
)
3333

3434

pre_commit/util.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
from __future__ import unicode_literals
22

33
import contextlib
4+
import errno
45
import functools
56
import os
67
import os.path
78
import shutil
9+
import stat
810
import subprocess
911
import tarfile
1012
import tempfile
1113

1214
import pkg_resources
1315

16+
from pre_commit import five
17+
1418

1519
@contextlib.contextmanager
1620
def 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

8488
def 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

Comments
 (0)