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

Skip to content

Commit 1996a4c

Browse files
committed
Make ^C^C during install not cause subsequent runs to fail. Resolves #186.
1 parent 901c506 commit 1996a4c

2 files changed

Lines changed: 44 additions & 1 deletion

File tree

pre_commit/repository.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from __future__ import unicode_literals
22

3+
import shutil
4+
35
from cached_property import cached_property
46

57
from pre_commit.languages.all import languages
@@ -64,11 +66,21 @@ def install(self):
6466
language = languages[language_name]
6567
if (
6668
language.ENVIRONMENT_DIR is None or
67-
self.cmd_runner.exists(language.ENVIRONMENT_DIR)
69+
self.cmd_runner.exists(language.ENVIRONMENT_DIR, '.installed')
6870
):
6971
# The language is already installed
7072
continue
73+
# There's potentially incomplete cleanup from previous runs
74+
# Clean it up!
75+
if self.cmd_runner.exists(language.ENVIRONMENT_DIR):
76+
shutil.rmtree(self.cmd_runner.path(language.ENVIRONMENT_DIR))
77+
7178
language.install_environment(self.cmd_runner, language_version)
79+
# Touch the .installed file (atomic) to indicate we've installed
80+
open(
81+
self.cmd_runner.path(language.ENVIRONMENT_DIR, '.installed'),
82+
'w',
83+
).close()
7284

7385
def run_hook(self, hook, file_args):
7486
"""Run a hook.

tests/repository_test.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33

44
import io
55
import os.path
6+
import shutil
67

78
import mock
89
import pytest
910

1011
from pre_commit.clientlib.validate_config import CONFIG_JSON_SCHEMA
1112
from pre_commit.clientlib.validate_config import validate_config_extra
1213
from pre_commit.jsonschema_extensions import apply_defaults
14+
from pre_commit.languages.python import PythonEnv
1315
from pre_commit.repository import Repository
1416
from pre_commit.util import cmd_output
1517
from pre_commit.util import cwd
@@ -266,6 +268,35 @@ def test_reinstall(tmpdir_factory, store):
266268
repo.require_installed()
267269

268270

271+
def test_control_c_control_c_on_install(tmpdir_factory, store):
272+
"""Regression test for #186."""
273+
path = make_repo(tmpdir_factory, 'python_hooks_repo')
274+
config = make_config_from_repo(path)
275+
repo = Repository.create(config, store)
276+
hook = repo.hooks[0][1]
277+
278+
class MyKeyboardInterrupt(KeyboardInterrupt):
279+
pass
280+
281+
# To simulate a killed install, we'll make PythonEnv.run raise ^C
282+
# and then to simulate a second ^C during cleanup, we'll make shutil.rmtree
283+
# raise as well.
284+
with pytest.raises(MyKeyboardInterrupt):
285+
with mock.patch.object(
286+
PythonEnv, 'run', side_effect=MyKeyboardInterrupt,
287+
):
288+
with mock.patch.object(shutil, 'rmtree', MyKeyboardInterrupt):
289+
repo.run_hook(hook, [])
290+
291+
# Should have made an environment, however this environment is broken!
292+
assert os.path.exists(repo.cmd_runner.path('py_env'))
293+
294+
# However, it should be perfectly runnable (reinstall after botched
295+
# install)
296+
retv, stdout, stderr = repo.run_hook(hook, [])
297+
assert retv == 0
298+
299+
269300
@pytest.mark.integration
270301
def test_really_long_file_paths(tmpdir_factory, store):
271302
base_path = tmpdir_factory.get()

0 commit comments

Comments
 (0)