3131INSTALLED_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+
3498class 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
199187class 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
219223class _UniqueList (list ):
0 commit comments