30
30
DOMAIN ,
31
31
FOLDER ,
32
32
LOGGER_PATH ,
33
+ REQUIREMENTS_FILE ,
34
+ REQUIREMENTS_PATHS ,
33
35
SERVICE_JUPYTER_KERNEL_START ,
34
36
UNSUB_LISTENERS ,
35
37
)
@@ -147,7 +149,7 @@ async def async_setup_entry(hass, config_entry):
147
149
148
150
State .set_pyscript_config (config_entry .data )
149
151
150
- await hass . async_add_executor_job ( install_requirements , hass )
152
+ await install_requirements ( hass )
151
153
await load_scripts (hass , config_entry .data )
152
154
153
155
async def reload_scripts_handler (call ):
@@ -165,7 +167,7 @@ async def reload_scripts_handler(call):
165
167
166
168
await unload_scripts (global_ctx_only = global_ctx_only )
167
169
168
- await hass . async_add_executor_job ( install_requirements , hass )
170
+ await install_requirements ( hass )
169
171
await load_scripts (hass , config_entry .data , global_ctx_only = global_ctx_only )
170
172
171
173
start_global_contexts (global_ctx_only = global_ctx_only )
@@ -266,55 +268,71 @@ async def unload_scripts(global_ctx_only=None, unload_all=False):
266
268
await GlobalContextMgr .delete (global_ctx_name )
267
269
268
270
271
+ @bind_hass
272
+ def load_all_requirement_lines (hass , requirements_paths , requirements_file ):
273
+ """Load all lines from requirements_file located in requirements_paths."""
274
+ all_requirements = {}
275
+ for root in requirements_paths :
276
+ for requirements_path in glob .glob (os .path .join (hass .config .path (FOLDER ), root , requirements_file )):
277
+ with open (requirements_path , "r" ) as requirements_fp :
278
+ all_requirements [requirements_path ] = requirements_fp .readlines ()
279
+
280
+ return all_requirements
281
+
282
+
269
283
@bind_hass
270
284
async def install_requirements (hass ):
271
285
"""Install missing requirements from requirements.txt."""
272
- for root in ("" , "apps/*" , "modules/*" ):
273
- for requirements_path in glob .glob (os .path .join (hass .config .path (FOLDER ), root , "requirements.txt" )):
274
- with open (requirements_path , "r" ) as requirements_file :
275
- requirements_to_install = []
276
- for pkg in requirements_file .readlines ():
277
- # Remove inline comments which are accepted by pip but not by Home
278
- # Assistant's installation method.
279
- # https://rosettacode.org/wiki/Strip_comments_from_a_string#Python
280
- i = pkg .find ("#" )
281
- if i >= 0 :
282
- pkg = pkg [:i ]
283
- pkg = pkg .strip ()
284
-
285
- try :
286
- # Attempt to get version of package. Do nothing if it's found since
287
- # we want to use the version that's already installed to be safe
288
- requirement = pkg_resources .Requirement .parse (pkg )
289
- requirement_installed_version = installed_version (requirement .project_name )
290
-
291
- if requirement_installed_version in requirement :
292
- _LOGGER .debug ("`%s` already found" , requirement .project_name )
293
- else :
294
- _LOGGER .warning (
295
- (
296
- "`%s` already found but found version `%s` does not"
297
- " match requirement. Keeping found version."
298
- ),
299
- requirement .project_name ,
300
- requirement_installed_version ,
301
- )
302
- except PackageNotFoundError :
303
- # Since package wasn't found, add it to installation list
304
- _LOGGER .debug ("%s not found, adding it to package installation list" , pkg )
305
- requirements_to_install .append (pkg )
306
- except ValueError :
307
- # Not valid requirements line so it can be skipped
308
- _LOGGER .debug ("Ignoring `%s` because it is not a valid package" , pkg )
309
- if requirements_to_install :
310
- _LOGGER .info (
311
- "Installing the following packages from %s: %s" ,
312
- requirements_path ,
313
- ", " .join (requirements_to_install ),
314
- )
315
- await async_process_requirements (hass , DOMAIN , requirements_to_install )
286
+ all_requirements = await hass .async_add_executor_job (
287
+ load_all_requirement_lines , hass , REQUIREMENTS_PATHS , REQUIREMENTS_FILE
288
+ )
289
+ requirements_to_install = []
290
+ for requirements_path , pkg_lines in all_requirements .items ():
291
+ for pkg in pkg_lines :
292
+ # Remove inline comments which are accepted by pip but not by Home
293
+ # Assistant's installation method.
294
+ # https://rosettacode.org/wiki/Strip_comments_from_a_string#Python
295
+ i = pkg .find ("#" )
296
+ if i >= 0 :
297
+ pkg = pkg [:i ]
298
+ pkg = pkg .strip ()
299
+
300
+ if not pkg :
301
+ continue
302
+
303
+ try :
304
+ # Attempt to get version of package. Do nothing if it's found since
305
+ # we want to use the version that's already installed to be safe
306
+ requirement = pkg_resources .Requirement .parse (pkg )
307
+ requirement_installed_version = installed_version (requirement .project_name )
308
+
309
+ if requirement_installed_version in requirement :
310
+ _LOGGER .debug ("`%s` already found" , requirement .project_name )
316
311
else :
317
- _LOGGER .debug ("All packages in %s are already available" , requirements_path )
312
+ _LOGGER .warning (
313
+ (
314
+ "`%s` already found but found version `%s` does not"
315
+ " match requirement. Keeping found version."
316
+ ),
317
+ requirement .project_name ,
318
+ requirement_installed_version ,
319
+ )
320
+ except PackageNotFoundError :
321
+ # Since package wasn't found, add it to installation list
322
+ _LOGGER .debug ("%s not found, adding it to package installation list" , pkg )
323
+ requirements_to_install .append (pkg )
324
+ except ValueError :
325
+ # Not valid requirements line so it can be skipped
326
+ _LOGGER .debug ("Ignoring `%s` because it is not a valid package" , pkg )
327
+ if requirements_to_install :
328
+ _LOGGER .info (
329
+ "Installing the following packages from %s: %s" ,
330
+ requirements_path ,
331
+ ", " .join (requirements_to_install ),
332
+ )
333
+ await async_process_requirements (hass , DOMAIN , requirements_to_install )
334
+ else :
335
+ _LOGGER .debug ("All packages in %s are already available" , requirements_path )
318
336
319
337
320
338
@bind_hass
0 commit comments