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

Skip to content

Commit 0478c60

Browse files
authored
Merge pull request #489 from pre-commit/local_language_hooks
Local language hooks
2 parents e5669ca + f000241 commit 0478c60

15 files changed

Lines changed: 240 additions & 221 deletions

File tree

pre_commit/commands/autoupdate.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def _update_repo(repo_config, runner, tags_only):
3232
"""
3333
repo = Repository.create(repo_config, runner.store)
3434

35-
with cwd(repo.repo_path_getter.repo_path):
35+
with cwd(repo._repo_path):
3636
cmd_output('git', 'fetch')
3737
tag_cmd = ('git', 'describe', 'origin/master', '--tags')
3838
if tags_only:

pre_commit/manifest.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,14 @@
1313

1414

1515
class Manifest(object):
16-
def __init__(self, repo_path_getter, repo_url):
17-
self.repo_path_getter = repo_path_getter
16+
def __init__(self, repo_path, repo_url):
17+
self.repo_path = repo_path
1818
self.repo_url = repo_url
1919

2020
@cached_property
2121
def manifest_contents(self):
22-
repo_path = self.repo_path_getter.repo_path
23-
default_path = os.path.join(repo_path, C.MANIFEST_FILE)
24-
legacy_path = os.path.join(repo_path, C.MANIFEST_FILE_LEGACY)
22+
default_path = os.path.join(self.repo_path, C.MANIFEST_FILE)
23+
legacy_path = os.path.join(self.repo_path, C.MANIFEST_FILE_LEGACY)
2524
if os.path.exists(legacy_path) and not os.path.exists(default_path):
2625
logger.warning(
2726
'{} uses legacy {} to provide hooks.\n'

pre_commit/repository.py

Lines changed: 122 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -31,41 +31,99 @@
3131
INSTALLED_STATE_VERSION = '1'
3232

3333

34+
def _state(additional_deps):
35+
return {'additional_dependencies': sorted(additional_deps)}
36+
37+
38+
def _state_filename(cmd_runner, venv):
39+
return cmd_runner.path(venv, '.install_state_v' + INSTALLED_STATE_VERSION)
40+
41+
42+
def _read_installed_state(cmd_runner, venv):
43+
filename = _state_filename(cmd_runner, venv)
44+
if not os.path.exists(filename):
45+
return None
46+
else:
47+
return json.loads(io.open(filename).read())
48+
49+
50+
def _write_installed_state(cmd_runner, venv, state):
51+
state_filename = _state_filename(cmd_runner, venv)
52+
staging = state_filename + 'staging'
53+
with io.open(staging, 'w') as state_file:
54+
state_file.write(five.to_text(json.dumps(state)))
55+
# Move the file into place atomically to indicate we've installed
56+
os.rename(staging, state_filename)
57+
58+
59+
def _installed(cmd_runner, language_name, language_version, additional_deps):
60+
language = languages[language_name]
61+
venv = environment_dir(language.ENVIRONMENT_DIR, language_version)
62+
return (
63+
venv is None or
64+
_read_installed_state(cmd_runner, venv) == _state(additional_deps)
65+
)
66+
67+
68+
def _install_all(venvs, repo_url):
69+
"""Tuple of (cmd_runner, language, version, deps)"""
70+
need_installed = tuple(
71+
(cmd_runner, language_name, version, deps)
72+
for cmd_runner, language_name, version, deps in venvs
73+
if not _installed(cmd_runner, language_name, version, deps)
74+
)
75+
76+
if need_installed:
77+
logger.info(
78+
'Installing environment for {}.'.format(repo_url)
79+
)
80+
logger.info('Once installed this environment will be reused.')
81+
logger.info('This may take a few minutes...')
82+
83+
for cmd_runner, language_name, version, deps in need_installed:
84+
language = languages[language_name]
85+
venv = environment_dir(language.ENVIRONMENT_DIR, version)
86+
87+
# There's potentially incomplete cleanup from previous runs
88+
# Clean it up!
89+
if cmd_runner.exists(venv):
90+
shutil.rmtree(cmd_runner.path(venv))
91+
92+
language.install_environment(cmd_runner, version, deps)
93+
# Write our state to indicate we're installed
94+
state = _state(deps)
95+
_write_installed_state(cmd_runner, venv, state)
96+
97+
3498
class Repository(object):
35-
def __init__(self, repo_config, repo_path_getter):
99+
def __init__(self, repo_config, store):
36100
self.repo_config = repo_config
37-
self.repo_path_getter = repo_path_getter
101+
self.store = store
38102
self.__installed = False
39103

40104
@classmethod
41105
def create(cls, config, store):
42106
if is_local_hooks(config):
43-
return LocalRepository(config)
107+
return LocalRepository(config, store)
44108
else:
45-
repo_path_getter = store.get_repo_path_getter(
46-
config['repo'], config['sha']
47-
)
48-
return cls(config, repo_path_getter)
109+
return cls(config, store)
49110

50111
@cached_property
51-
def repo_url(self):
52-
return self.repo_config['repo']
112+
def _repo_path(self):
113+
return self.store.clone(
114+
self.repo_config['repo'], self.repo_config['sha'],
115+
)
53116

54117
@cached_property
55-
def languages(self):
56-
return {
57-
(hook['language'], hook['language_version'])
58-
for _, hook in self.hooks
59-
}
118+
def _cmd_runner(self):
119+
return PrefixedCommandRunner(self._repo_path)
120+
121+
def _cmd_runner_from_deps(self, language_name, deps):
122+
return self._cmd_runner
60123

61124
@cached_property
62-
def additional_dependencies(self):
63-
dep_dict = defaultdict(lambda: defaultdict(_UniqueList))
64-
for _, hook in self.hooks:
65-
dep_dict[hook['language']][hook['language_version']].update(
66-
hook.get('additional_dependencies', []),
67-
)
68-
return dep_dict
125+
def manifest(self):
126+
return Manifest(self._repo_path, self.repo_config['repo'])
69127

70128
@cached_property
71129
def hooks(self):
@@ -97,108 +155,49 @@ def hooks(self):
97155
)
98156

99157
@cached_property
100-
def manifest(self):
101-
return Manifest(self.repo_path_getter, self.repo_url)
102-
103-
@cached_property
104-
def cmd_runner(self):
105-
return PrefixedCommandRunner(self.repo_path_getter.repo_path)
106-
107-
def require_installed(self):
108-
if self.__installed:
109-
return
110-
111-
self.install()
112-
self.__installed = True
113-
114-
def install(self):
115-
"""Install the hook repository."""
116-
def state(language_name, language_version):
117-
return {
118-
'additional_dependencies': sorted(
119-
self.additional_dependencies[
120-
language_name
121-
][language_version],
122-
)
123-
}
124-
125-
def state_filename(venv, suffix=''):
126-
return self.cmd_runner.path(
127-
venv, '.install_state_v' + INSTALLED_STATE_VERSION + suffix,
128-
)
129-
130-
def read_state(venv):
131-
if not os.path.exists(state_filename(venv)):
132-
return None
133-
else:
134-
return json.loads(io.open(state_filename(venv)).read())
135-
136-
def write_state(venv, language_name, language_version):
137-
with io.open(
138-
state_filename(venv, suffix='staging'), 'w',
139-
) as state_file:
140-
state_file.write(five.to_text(json.dumps(
141-
state(language_name, language_version),
142-
)))
143-
# Move the file into place atomically to indicate we've installed
144-
os.rename(
145-
state_filename(venv, suffix='staging'),
146-
state_filename(venv),
147-
)
148-
149-
def language_is_installed(language_name, language_version):
150-
language = languages[language_name]
151-
venv = environment_dir(language.ENVIRONMENT_DIR, language_version)
152-
return (
153-
venv is None or
154-
read_state(venv) == state(language_name, language_version)
155-
)
156-
157-
if not all(
158-
language_is_installed(language_name, language_version)
159-
for language_name, language_version in self.languages
160-
):
161-
logger.info(
162-
'Installing environment for {}.'.format(self.repo_url)
158+
def _venvs(self):
159+
deps_dict = defaultdict(_UniqueList)
160+
for _, hook in self.hooks:
161+
deps_dict[(hook['language'], hook['language_version'])].update(
162+
hook.get('additional_dependencies', []),
163163
)
164-
logger.info('Once installed this environment will be reused.')
165-
logger.info('This may take a few minutes...')
166-
167-
for language_name, language_version in self.languages:
168-
if language_is_installed(language_name, language_version):
169-
continue
170-
171-
language = languages[language_name]
172-
venv = environment_dir(language.ENVIRONMENT_DIR, language_version)
173-
174-
# There's potentially incomplete cleanup from previous runs
175-
# Clean it up!
176-
if self.cmd_runner.exists(venv):
177-
shutil.rmtree(self.cmd_runner.path(venv))
164+
ret = []
165+
for (language, version), deps in deps_dict.items():
166+
ret.append((self._cmd_runner, language, version, deps))
167+
return tuple(ret)
178168

179-
language.install_environment(
180-
self.cmd_runner, language_version,
181-
self.additional_dependencies[language_name][language_version],
182-
)
183-
# Write our state to indicate we're installed
184-
write_state(venv, language_name, language_version)
169+
def require_installed(self):
170+
if not self.__installed:
171+
_install_all(self._venvs, self.repo_config['repo'])
172+
self.__installed = True
185173

186174
def run_hook(self, hook, file_args):
187175
"""Run a hook.
188176
189-
Args:
190-
hook - Hook dictionary
191-
file_args - List of files to run
177+
:param dict hook:
178+
:param tuple file_args: all the files to run the hook on
192179
"""
193180
self.require_installed()
194-
return languages[hook['language']].run_hook(
195-
self.cmd_runner, hook, file_args,
196-
)
181+
language_name = hook['language']
182+
deps = hook.get('additional_dependencies', [])
183+
cmd_runner = self._cmd_runner_from_deps(language_name, deps)
184+
return languages[language_name].run_hook(cmd_runner, hook, file_args)
197185

198186

199187
class LocalRepository(Repository):
200-
def __init__(self, repo_config):
201-
super(LocalRepository, self).__init__(repo_config, None)
188+
def _cmd_runner_from_deps(self, language_name, deps):
189+
"""local repositories have a cmd runner per hook"""
190+
language = languages[language_name]
191+
# pcre / script / system do not have environments so they work out
192+
# of the current directory
193+
if language.ENVIRONMENT_DIR is None:
194+
return PrefixedCommandRunner(git.get_root())
195+
else:
196+
return PrefixedCommandRunner(self.store.make_local(deps))
197+
198+
@cached_property
199+
def manifest(self):
200+
raise NotImplementedError
202201

203202
@cached_property
204203
def hooks(self):
@@ -208,12 +207,17 @@ def hooks(self):
208207
)
209208

210209
@cached_property
211-
def cmd_runner(self):
212-
return PrefixedCommandRunner(git.get_root())
213-
214-
@cached_property
215-
def manifest(self):
216-
raise NotImplementedError
210+
def _venvs(self):
211+
ret = []
212+
for _, hook in self.hooks:
213+
language = hook['language']
214+
version = hook['language_version']
215+
deps = hook.get('additional_dependencies', [])
216+
ret.append((
217+
self._cmd_runner_from_deps(language, deps),
218+
language, version, deps,
219+
))
220+
return tuple(ret)
217221

218222

219223
class _UniqueList(list):
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package main
2+
3+
func main() {}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"name": "pre_commit_dummy_package",
3+
"version": "0.0.0"
4+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Gem::Specification.new do |s|
2+
s.name = 'pre_commit_dummy_package'
3+
s.version = '0.0.0'
4+
s.authors = ['Anthony Sottile']
5+
end
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from setuptools import setup
2+
3+
4+
setup(name='pre-commit-dummy-package', version='0.0.0')

0 commit comments

Comments
 (0)