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

Skip to content

Commit a116a3f

Browse files
committed
Merge pull request #49 from pre-commit/fix_cwd_problem
Fix cwd problem
2 parents 45e6a0b + 53317ad commit a116a3f

18 files changed

Lines changed: 434 additions & 118 deletions

File tree

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ python:
55
- 2.7
66

77
install: pip install virtualenv
8-
script: make
8+
script: make coverage
99

1010

1111
before_install:

pre_commit/languages/all.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,22 @@
55

66
# A language implements the following two functions in its module:
77
#
8-
# def install_environment():
8+
# def install_environment(repo_cmd_runner):
99
# """Installs a repository in the given repository. Note that the current
1010
# working directory will already be inside the repository.
11+
#
12+
# Args:
13+
# repo_cmd_runner - `PrefixedCommandRunner` bound to the repository.
1114
# """
1215
#
13-
# def run_hook(hook, file_args):
16+
# def run_hook(repo_cmd_runner, hook, file_args):
1417
# """Runs a hook and returns the returncode and output of running that hook.
1518
#
19+
# Args:
20+
# repo_cmd_runner - `PrefixedCommandRunner` bound to the repository.
21+
# hook - Hook dictionary
22+
# file_args - The files to be run
23+
#
1624
# Returns:
1725
# (returncode, stdout, stderr)
1826
# """

pre_commit/languages/helpers.py

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11

2-
import subprocess
3-
42

53
def run_hook(env, hook, file_args):
64
return env.run(
75
' '.join(['xargs', hook['entry']] + hook.get('args', [])),
86
stdin='\n'.join(list(file_args) + ['']),
7+
retcode=None,
98
)
109

1110

1211
class Environment(object):
12+
def __init__(self, repo_cmd_runner):
13+
self.repo_cmd_runner = repo_cmd_runner
14+
1315
@property
1416
def env_prefix(self):
1517
"""env_prefix is a value that is prefixed to the command that is run.
@@ -24,14 +26,8 @@ def env_prefix(self):
2426
"""
2527
raise NotImplementedError
2628

27-
def run(self, cmd, stdin=None):
29+
def run(self, cmd, **kwargs):
2830
"""Returns (returncode, stdout, stderr)."""
29-
proc = subprocess.Popen(
30-
['bash', '-c', ' '.join([self.env_prefix, cmd])],
31-
stdin=subprocess.PIPE,
32-
stdout=subprocess.PIPE,
33-
stderr=subprocess.PIPE,
31+
return self.repo_cmd_runner.run(
32+
['bash', '-c', ' '.join([self.env_prefix, cmd])], **kwargs
3433
)
35-
stdout, stderr = proc.communicate(stdin)
36-
37-
return proc.returncode, stdout, stderr

pre_commit/languages/node.py

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import contextlib
2-
from plumbum import local
32

43
from pre_commit.languages import helpers
54
from pre_commit.languages import python
5+
from pre_commit.prefixed_command_runner import CalledProcessError
66

77

88
NODE_ENV = 'node_env'
@@ -12,37 +12,42 @@ class NodeEnv(python.PythonEnv):
1212
@property
1313
def env_prefix(self):
1414
base = super(NodeEnv, self).env_prefix
15-
return ' '.join([base, '. {0}/bin/activate &&'.format(NODE_ENV)])
15+
return ' '.join([
16+
base,
17+
'. {{prefix}}{0}/bin/activate &&'.format(NODE_ENV)]
18+
)
1619

1720

1821
@contextlib.contextmanager
19-
def in_env():
20-
yield NodeEnv()
22+
def in_env(repo_cmd_runner):
23+
yield NodeEnv(repo_cmd_runner)
2124

2225

23-
def install_environment():
24-
assert local.path('package.json').exists()
26+
def install_environment(repo_cmd_runner):
27+
assert repo_cmd_runner.exists('package.json')
2528

26-
if local.path(NODE_ENV).exists():
29+
# Return immediately if we already have a virtualenv
30+
if repo_cmd_runner.exists(NODE_ENV):
2731
return
2832

29-
local['virtualenv'][python.PY_ENV]()
33+
repo_cmd_runner.run(['virtualenv', '{{prefix}}{0}'.format(python.PY_ENV)])
3034

31-
with python.in_env() as python_env:
35+
with python.in_env(repo_cmd_runner) as python_env:
3236
python_env.run('pip install nodeenv')
3337

3438
# Try and use the system level node executable first
35-
retcode, _, _ = python_env.run('nodeenv -n system {0}'.format(NODE_ENV))
36-
# TODO: log failure here
37-
# cleanup
38-
if retcode:
39-
local.path(NODE_ENV).delete()
40-
python_env.run('nodeenv --jobs 4 {0}'.format(NODE_ENV))
39+
try:
40+
python_env.run('nodeenv -n system {{prefix}}{0}'.format(NODE_ENV))
41+
except CalledProcessError:
42+
# TODO: log failure here
43+
# cleanup
44+
# TODO: local.path(NODE_ENV).delete()
45+
python_env.run('nodeenv --jobs 4 {{prefix}}{0}'.format(NODE_ENV))
4146

42-
with in_env() as node_env:
43-
node_env.run('npm install -g')
47+
with in_env(repo_cmd_runner) as node_env:
48+
node_env.run('cd {prefix} && npm install -g')
4449

4550

46-
def run_hook(hook, file_args):
47-
with in_env() as node_env:
48-
return helpers.run_hook(node_env, hook, file_args)
51+
def run_hook(repo_cmd_runner, hook, file_args):
52+
with in_env(repo_cmd_runner) as env:
53+
return helpers.run_hook(env, hook, file_args)

pre_commit/languages/python.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,35 @@
11

22
import contextlib
3-
from plumbum import local
3+
44
from pre_commit.languages import helpers
55

6+
67
PY_ENV = 'py_env'
78

89

910
class PythonEnv(helpers.Environment):
1011
@property
1112
def env_prefix(self):
12-
return '. {0}/bin/activate &&'.format(PY_ENV)
13+
return '. {{prefix}}{0}/bin/activate &&'.format(PY_ENV)
1314

1415

1516
@contextlib.contextmanager
16-
def in_env():
17-
yield PythonEnv()
17+
def in_env(repo_cmd_runner):
18+
yield PythonEnv(repo_cmd_runner)
1819

1920

20-
def install_environment():
21-
assert local.path('setup.py').exists()
21+
def install_environment(repo_cmd_runner):
22+
assert repo_cmd_runner.exists('setup.py')
2223
# Return immediately if we already have a virtualenv
23-
if local.path(PY_ENV).exists():
24+
if repo_cmd_runner.exists(PY_ENV):
2425
return
2526

2627
# Install a virtualenv
27-
local['virtualenv'][PY_ENV]()
28-
with in_env() as env:
29-
env.run('pip install .')
28+
repo_cmd_runner.run(['virtualenv', '{{prefix}}{0}'.format(PY_ENV)])
29+
with in_env(repo_cmd_runner) as env:
30+
env.run('cd {prefix} && pip install .')
3031

3132

32-
def run_hook(hook, file_args):
33-
with in_env() as env:
33+
def run_hook(repo_cmd_runner, hook, file_args):
34+
with in_env(repo_cmd_runner) as env:
3435
return helpers.run_hook(env, hook, file_args)

pre_commit/languages/ruby.py

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,33 @@
11

22
import contextlib
3-
from plumbum import local
43

54
from pre_commit.languages import helpers
65

76

87
RVM_ENV = 'rvm_env'
98

109

11-
class RubyEnv(object):
12-
def __init__(self):
13-
self.env_prefix = '. {0}/.rvm/scripts/rvm'.format(RVM_ENV)
14-
15-
def run(self, cmd, **kwargs):
16-
return local['bash']['-c', ' '.join([self.env_prefix, cmd])].run(**kwargs)
10+
class RubyEnv(helpers.Environment):
11+
@property
12+
def env_prefix(self):
13+
return '. {{prefix}}{0}/bin/activate &&'.format(RVM_ENV)
1714

1815

1916
@contextlib.contextmanager
20-
def in_env():
21-
yield RubyEnv()
17+
def in_env(repo_cmd_runner):
18+
yield RubyEnv(repo_cmd_runner)
2219

2320

24-
def install_environment():
21+
def install_environment(repo_cmd_runner):
2522
# Return immediately if we already have a virtualenv
26-
if local.path(RVM_ENV).exists():
23+
if repo_cmd_runner.exists(RVM_ENV):
2724
return
2825

29-
local['__rvm-env.sh'][RVM_ENV]()
30-
with in_env() as env:
31-
env.run('bundle install')
26+
repo_cmd_runner.run(['__rvm-env.sh', '{{prefix}}{0}'.format(RVM_ENV)])
27+
with in_env(repo_cmd_runner) as env:
28+
env.run('cd {prefix} && bundle install')
3229

3330

34-
def run_hook(hook, file_args):
35-
with in_env() as env:
31+
def run_hook(repo_cmd_runner, hook, file_args):
32+
with in_env(repo_cmd_runner) as env:
3633
return helpers.run_hook(env, hook, file_args)
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
2+
import os
3+
import os.path
4+
import subprocess
5+
6+
7+
class CalledProcessError(RuntimeError):
8+
def __init__(self, returncode, cmd, expected_returncode, output=None):
9+
self.returncode = returncode
10+
self.cmd = cmd
11+
self.expected_returncode = expected_returncode
12+
self.output = output
13+
14+
def __str__(self):
15+
return (
16+
'Command: {0!r}\n'
17+
'Return code: {1}\n'
18+
'Expected return code {2}\n',
19+
'Output: {3!r}\n'.format(
20+
self.cmd,
21+
self.returncode,
22+
self.expected_returncode,
23+
self.output,
24+
),
25+
)
26+
27+
28+
def _replace_cmd(cmd, **kwargs):
29+
return [part.format(**kwargs) for part in cmd]
30+
31+
32+
class PrefixedCommandRunner(object):
33+
"""A PrefixedCommandRunner allows you to run subprocess commands with
34+
comand substitution.
35+
36+
For instance:
37+
PrefixedCommandRunner('/tmp/foo').run(['{prefix}foo.sh', 'bar', 'baz'])
38+
39+
will run ['/tmpl/foo/foo.sh', 'bar', 'baz']
40+
"""
41+
def __init__(self, prefix_dir, popen=subprocess.Popen, makedirs=os.makedirs):
42+
self.prefix_dir = prefix_dir.rstrip(os.sep) + os.sep
43+
self.__popen = popen
44+
self.__makedirs = makedirs
45+
46+
def _create_path_if_not_exists(self):
47+
if not os.path.exists(self.prefix_dir):
48+
self.__makedirs(self.prefix_dir)
49+
50+
def run(self, cmd, retcode=0, stdin=None, **kwargs):
51+
self._create_path_if_not_exists()
52+
replaced_cmd = _replace_cmd(cmd, prefix=self.prefix_dir)
53+
proc = self.__popen(
54+
replaced_cmd,
55+
stdin=subprocess.PIPE,
56+
stdout=subprocess.PIPE,
57+
stderr=subprocess.PIPE,
58+
**kwargs
59+
)
60+
stdout, stderr = proc.communicate(stdin)
61+
returncode = proc.returncode
62+
63+
if retcode is not None and retcode != returncode:
64+
raise CalledProcessError(
65+
returncode, replaced_cmd, retcode, output=(stdout, stderr),
66+
)
67+
68+
return proc.returncode, stdout, stderr
69+
70+
def path(self, *parts):
71+
path = os.path.join(self.prefix_dir, *parts)
72+
return os.path.normpath(path)
73+
74+
def exists(self, *parts):
75+
return os.path.exists(self.path(*parts))
76+
77+
@classmethod
78+
def from_command_runner(cls, command_runner, path_end):
79+
"""Constructs a new command runner from an existing one by appending
80+
`path_end` to the command runner's prefix directory.
81+
"""
82+
return cls(command_runner.path(path_end), popen=command_runner.__popen)

0 commit comments

Comments
 (0)