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 ):
3599 def __init__ (self , repo_config , repo_path_getter ):
36100 self .repo_config = repo_config
@@ -48,24 +112,24 @@ def create(cls, config, store):
48112 return cls (config , repo_path_getter )
49113
50114 @cached_property
51- def repo_url (self ):
52- return self .repo_config ['repo' ]
53-
54- @cached_property
55- def languages (self ):
56- return {
57- (hook ['language' ], hook ['language_version' ])
58- for _ , hook in self .hooks
59- }
115+ def _cmd_runner (self ):
116+ return PrefixedCommandRunner (self .repo_path_getter .repo_path )
60117
61118 @cached_property
62- def additional_dependencies (self ):
63- dep_dict = defaultdict (lambda : defaultdict ( _UniqueList ) )
119+ def _venvs (self ):
120+ deps_dict = defaultdict (_UniqueList )
64121 for _ , hook in self .hooks :
65- dep_dict [ hook ['language' ]][ hook ['language_version' ]].update (
122+ deps_dict [( hook ['language' ], hook ['language_version' ]) ].update (
66123 hook .get ('additional_dependencies' , []),
67124 )
68- return dep_dict
125+ ret = []
126+ for (language , version ), deps in deps_dict .items ():
127+ ret .append ((self ._cmd_runner , language , version , deps ))
128+ return tuple (ret )
129+
130+ @cached_property
131+ def manifest (self ):
132+ return Manifest (self .repo_path_getter , self .repo_config ['repo' ])
69133
70134 @cached_property
71135 def hooks (self ):
@@ -96,92 +160,10 @@ def hooks(self):
96160 for hook in self .repo_config ['hooks' ]
97161 )
98162
99- @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-
107163 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 )
163- )
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 ))
178-
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 )
164+ if not self .__installed :
165+ _install_all (self ._venvs , self .repo_config ['repo' ])
166+ self .__installed = True
185167
186168 def run_hook (self , hook , file_args ):
187169 """Run a hook.
@@ -192,7 +174,7 @@ def run_hook(self, hook, file_args):
192174 """
193175 self .require_installed ()
194176 return languages [hook ['language' ]].run_hook (
195- self .cmd_runner , hook , file_args ,
177+ self ._cmd_runner , hook , file_args ,
196178 )
197179
198180
0 commit comments