Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e944bb6

Browse files
committed
Merge pull request #196 from pre-commit/windoze
Windows MVP
2 parents 3babceb + 161cff7 commit e944bb6

22 files changed

Lines changed: 314 additions & 143 deletions

appveyor.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
environment:
2+
matrix:
3+
- TOXENV: py27
4+
- TOXENV: py34
5+
6+
install:
7+
- "SET PATH=C:\\Python34;C:\\Python34\\Scripts;%PATH%"
8+
- pip install tox
9+
10+
# Not a C# project
11+
build: false
12+
13+
before_test:
14+
- git config --global user.name "AppVeyor CI"
15+
- git config --global user.email "[email protected]"
16+
17+
# Workaround for http://help.appveyor.com/discussions/problems/1531-having-issues-with-configured-git-bash
18+
test_script: bash -c tox

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: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
from __future__ import unicode_literals
22

3+
import contextlib
34
import io
45
import logging
56
import os
67
import os.path
8+
import sqlite3
79
import tempfile
810

911
from cached_property import cached_property
@@ -12,7 +14,6 @@
1214
from pre_commit.util import clean_path_on_failure
1315
from pre_commit.util import cmd_output
1416
from pre_commit.util import cwd
15-
from pre_commit.util import hex_md5
1617

1718

1819
logger = logging.getLogger('pre_commit')
@@ -27,7 +28,7 @@ def _get_default_directory():
2728
"""
2829
return os.environ.get(
2930
'PRE_COMMIT_HOME',
30-
os.path.join(os.environ['HOME'], '.pre-commit'),
31+
os.path.join(os.path.expanduser('~'), '.pre-commit'),
3132
)
3233

3334

@@ -58,11 +59,35 @@ def _write_readme(self):
5859
'Learn more: https://github.com/pre-commit/pre-commit\n'
5960
)
6061

62+
def _write_sqlite_db(self):
63+
# To avoid a race where someone ^Cs between db creation and execution
64+
# of the CREATE TABLE statement
65+
fd, tmpfile = tempfile.mkstemp()
66+
# We'll be managing this file ourselves
67+
os.close(fd)
68+
# sqlite doesn't close its fd with its contextmanager >.<
69+
# contextlib.closing fixes this.
70+
# See: http://stackoverflow.com/a/28032829/812183
71+
with contextlib.closing(sqlite3.connect(tmpfile)) as db:
72+
db.executescript(
73+
'CREATE TABLE repos ('
74+
' repo CHAR(255) NOT NULL,'
75+
' ref CHAR(255) NOT NULL,'
76+
' path CHAR(255) NOT NULL,'
77+
' PRIMARY KEY (repo, ref)'
78+
');'
79+
)
80+
81+
# Atomic file move
82+
os.rename(tmpfile, self.db_path)
83+
6184
def _create(self):
62-
if os.path.exists(self.directory):
85+
if os.path.exists(self.db_path):
6386
return
64-
os.makedirs(self.directory)
65-
self._write_readme()
87+
if not os.path.exists(self.directory):
88+
os.makedirs(self.directory)
89+
self._write_readme()
90+
self._write_sqlite_db()
6691

6792
def require_created(self):
6893
"""Require the pre-commit file store to be created."""
@@ -77,9 +102,13 @@ def clone(self, url, sha):
77102
self.require_created()
78103

79104
# Check if we already exist
80-
sha_path = os.path.join(self.directory, sha + '_' + hex_md5(url))
81-
if os.path.exists(sha_path):
82-
return os.readlink(sha_path)
105+
with sqlite3.connect(self.db_path) as db:
106+
result = db.execute(
107+
'SELECT path FROM repos WHERE repo = ? AND ref = ?',
108+
[url, sha],
109+
).fetchone()
110+
if result:
111+
return result[0]
83112

84113
logger.info('Initializing environment for {0}.'.format(url))
85114

@@ -89,8 +118,12 @@ def clone(self, url, sha):
89118
with cwd(dir):
90119
cmd_output('git', 'checkout', sha)
91120

92-
# Make a symlink from sha->repo
93-
os.symlink(dir, sha_path)
121+
# Update our db with the created repo
122+
with sqlite3.connect(self.db_path) as db:
123+
db.execute(
124+
'INSERT INTO repos (repo, ref, path) VALUES (?, ?, ?)',
125+
[url, sha, dir],
126+
)
94127
return dir
95128

96129
def get_repo_path_getter(self, repo, sha):
@@ -99,3 +132,7 @@ def get_repo_path_getter(self, repo, sha):
99132
@cached_property
100133
def cmd_runner(self):
101134
return PrefixedCommandRunner(self.directory)
135+
136+
@cached_property
137+
def db_path(self):
138+
return os.path.join(self.directory, 'db.db')

0 commit comments

Comments
 (0)