4
4
import json
5
5
import logging
6
6
import os
7
- import sys
8
7
9
- import pkg_resources
10
8
import voluptuous as vol
11
9
12
10
from homeassistant .config import async_hass_config_yaml
21
19
import homeassistant .helpers .config_validation as cv
22
20
from homeassistant .helpers .restore_state import RestoreStateData
23
21
from homeassistant .loader import bind_hass
24
- from homeassistant .requirements import async_process_requirements
25
22
26
23
from .const import (
27
24
CONF_ALLOW_ALL_IMPORTS ,
30
27
DOMAIN ,
31
28
FOLDER ,
32
29
LOGGER_PATH ,
33
- REQUIREMENTS_FILE ,
34
- REQUIREMENTS_PATHS ,
35
30
SERVICE_JUPYTER_KERNEL_START ,
36
31
UNSUB_LISTENERS ,
37
32
)
40
35
from .function import Function
41
36
from .global_ctx import GlobalContext , GlobalContextMgr
42
37
from .jupyter_kernel import Kernel
38
+ from .requirements import install_requirements
43
39
from .state import State
44
40
from .trigger import TrigTime
45
41
46
- if sys .version_info [:2 ] >= (3 , 8 ):
47
- from importlib .metadata import ( # pylint: disable=no-name-in-module,import-error
48
- PackageNotFoundError ,
49
- version as installed_version ,
50
- )
51
- else :
52
- from importlib_metadata import ( # pylint: disable=import-error
53
- PackageNotFoundError ,
54
- version as installed_version ,
55
- )
56
-
57
42
_LOGGER = logging .getLogger (LOGGER_PATH )
58
43
59
44
PYSCRIPT_SCHEMA = vol .Schema (
@@ -149,7 +134,7 @@ async def async_setup_entry(hass, config_entry):
149
134
150
135
State .set_pyscript_config (config_entry .data )
151
136
152
- await install_requirements (hass )
137
+ await install_requirements (hass , config_entry , pyscript_folder )
153
138
await load_scripts (hass , config_entry .data )
154
139
155
140
async def reload_scripts_handler (call ):
@@ -169,7 +154,7 @@ async def reload_scripts_handler(call):
169
154
170
155
await unload_scripts (global_ctx_only = global_ctx_only )
171
156
172
- await install_requirements (hass )
157
+ await install_requirements (hass , config_entry , pyscript_folder )
173
158
await load_scripts (hass , config_entry .data , global_ctx_only = global_ctx_only )
174
159
175
160
start_global_contexts (global_ctx_only = global_ctx_only )
@@ -271,81 +256,6 @@ async def unload_scripts(global_ctx_only=None, unload_all=False):
271
256
await GlobalContextMgr .delete (global_ctx_name )
272
257
273
258
274
- @bind_hass
275
- def process_all_requirements (hass , requirements_paths , requirements_file ):
276
- """
277
- Load all lines from requirements_file located in requirements_paths.
278
-
279
- Returns files and a list of packages, if any, that need to be installed.
280
- """
281
- all_requirements_to_process = {}
282
- for root in requirements_paths :
283
- for requirements_path in glob .glob (os .path .join (hass .config .path (FOLDER ), root , requirements_file )):
284
- with open (requirements_path , "r" ) as requirements_fp :
285
- all_requirements_to_process [requirements_path ] = requirements_fp .readlines ()
286
-
287
- all_requirements_to_install = {}
288
- for requirements_path , pkg_lines in all_requirements_to_process .items ():
289
- all_requirements_to_install [requirements_path ] = []
290
- for pkg in pkg_lines :
291
- # Remove inline comments which are accepted by pip but not by Home
292
- # Assistant's installation method.
293
- # https://rosettacode.org/wiki/Strip_comments_from_a_string#Python
294
- i = pkg .find ("#" )
295
- if i >= 0 :
296
- pkg = pkg [:i ]
297
- pkg = pkg .strip ()
298
-
299
- if not pkg :
300
- continue
301
-
302
- try :
303
- # Attempt to get version of package. Do nothing if it's found since
304
- # we want to use the version that's already installed to be safe
305
- requirement = pkg_resources .Requirement .parse (pkg )
306
- requirement_installed_version = installed_version (requirement .project_name )
307
-
308
- if requirement_installed_version in requirement :
309
- _LOGGER .debug ("`%s` already found" , requirement .project_name )
310
- else :
311
- _LOGGER .warning (
312
- (
313
- "`%s` already found but found version `%s` does not"
314
- " match requirement. Keeping found version."
315
- ),
316
- requirement .project_name ,
317
- requirement_installed_version ,
318
- )
319
- except PackageNotFoundError :
320
- # Since package wasn't found, add it to installation list
321
- _LOGGER .debug ("%s not found, adding it to package installation list" , pkg )
322
- all_requirements_to_install [requirements_path ].append (pkg )
323
- except ValueError :
324
- # Not valid requirements line so it can be skipped
325
- _LOGGER .debug ("Ignoring `%s` because it is not a valid package" , pkg )
326
-
327
- return all_requirements_to_install
328
-
329
-
330
- @bind_hass
331
- async def install_requirements (hass ):
332
- """Install missing requirements from requirements.txt."""
333
- all_requirements = await hass .async_add_executor_job (
334
- process_all_requirements , hass , REQUIREMENTS_PATHS , REQUIREMENTS_FILE
335
- )
336
-
337
- for requirements_path , requirements_to_install in all_requirements .items ():
338
- if requirements_to_install :
339
- _LOGGER .info (
340
- "Installing the following packages from %s: %s" ,
341
- requirements_path ,
342
- ", " .join (requirements_to_install ),
343
- )
344
- await async_process_requirements (hass , DOMAIN , requirements_to_install )
345
- else :
346
- _LOGGER .debug ("All packages in %s are already available" , requirements_path )
347
-
348
-
349
259
@bind_hass
350
260
async def load_scripts (hass , data , global_ctx_only = None ):
351
261
"""Load all python scripts in FOLDER."""
0 commit comments