Skip to content

Commit 60232a3

Browse files
authored
Merge pull request #69 from raman325/install_packages
Refactor logic for processing requirements.txt so all I/O operations happen in a single function
2 parents b8f52e3 + 4babcf5 commit 60232a3

File tree

6 files changed

+82
-71
lines changed

6 files changed

+82
-71
lines changed

custom_components/pyscript/__init__.py

+24-16
Original file line numberDiff line numberDiff line change
@@ -272,25 +272,21 @@ async def unload_scripts(global_ctx_only=None, unload_all=False):
272272

273273

274274
@bind_hass
275-
def load_all_requirement_lines(hass, requirements_paths, requirements_file):
276-
"""Load all lines from requirements_file located in requirements_paths."""
277-
all_requirements = {}
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 = {}
278282
for root in requirements_paths:
279283
for requirements_path in glob.glob(os.path.join(hass.config.path(FOLDER), root, requirements_file)):
280284
with open(requirements_path, "r") as requirements_fp:
281-
all_requirements[requirements_path] = requirements_fp.readlines()
282-
283-
return all_requirements
284-
285+
all_requirements_to_process[requirements_path] = requirements_fp.readlines()
285286

286-
@bind_hass
287-
async def install_requirements(hass):
288-
"""Install missing requirements from requirements.txt."""
289-
all_requirements = await hass.async_add_executor_job(
290-
load_all_requirement_lines, hass, REQUIREMENTS_PATHS, REQUIREMENTS_FILE
291-
)
292-
requirements_to_install = []
293-
for requirements_path, pkg_lines in all_requirements.items():
287+
all_requirements_to_install = {}
288+
for requirements_path, pkg_lines in all_requirements_to_process.items():
289+
all_requirements_to_install[requirements_path] = []
294290
for pkg in pkg_lines:
295291
# Remove inline comments which are accepted by pip but not by Home
296292
# Assistant's installation method.
@@ -323,10 +319,22 @@ async def install_requirements(hass):
323319
except PackageNotFoundError:
324320
# Since package wasn't found, add it to installation list
325321
_LOGGER.debug("%s not found, adding it to package installation list", pkg)
326-
requirements_to_install.append(pkg)
322+
all_requirements_to_install[requirements_path].append(pkg)
327323
except ValueError:
328324
# Not valid requirements line so it can be skipped
329325
_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():
330338
if requirements_to_install:
331339
_LOGGER.info(
332340
"Installing the following packages from %s: %s",

tests/test_decorator_errors.py

+2-9
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,8 @@ async def setup_script(hass, notify_q, now, source):
2424
), patch(
2525
"homeassistant.config.load_yaml_config_file", return_value={}
2626
), patch(
27-
"custom_components.pyscript.load_all_requirement_lines",
28-
return_value={
29-
"/some/config/dir/pyscript/requirements.txt": [
30-
"pytube==9.7.0\n",
31-
"# another test comment\n",
32-
"pykakasi==2.0.1 # test comment\n",
33-
"\n",
34-
]
35-
},
27+
"custom_components.pyscript.process_all_requirements",
28+
return_value={"/some/config/dir/pyscript/requirements.txt": ["pytube==2.0.1", "pykakasi==2.0.1"]},
3629
):
3730
assert await async_setup_component(hass, "pyscript", {DOMAIN: {}})
3831

tests/test_function.py

+2-9
Original file line numberDiff line numberDiff line change
@@ -111,15 +111,8 @@ async def setup_script(hass, notify_q, now, source):
111111
), patch(
112112
"homeassistant.config.load_yaml_config_file", return_value={DOMAIN: {CONF_ALLOW_ALL_IMPORTS: True}}
113113
), patch(
114-
"custom_components.pyscript.load_all_requirement_lines",
115-
return_value={
116-
"/some/config/dir/pyscript/requirements.txt": [
117-
"pytube==9.7.0\n",
118-
"# another test comment\n",
119-
"pykakasi==2.0.1 # test comment\n",
120-
"\n",
121-
]
122-
},
114+
"custom_components.pyscript.process_all_requirements",
115+
return_value={"/some/config/dir/pyscript/requirements.txt": ["pytube==2.0.1", "pykakasi==2.0.1"]},
123116
):
124117
assert await async_setup_component(hass, "pyscript", {DOMAIN: {CONF_ALLOW_ALL_IMPORTS: True}})
125118

tests/test_init.py

+50-19
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
from datetime import datetime as dt
55
import pathlib
66

7-
from custom_components.pyscript.const import DOMAIN
7+
from custom_components.pyscript import process_all_requirements
8+
from custom_components.pyscript.const import DOMAIN, REQUIREMENTS_FILE, REQUIREMENTS_PATHS
89
from custom_components.pyscript.event import Event
910
from custom_components.pyscript.function import Function
1011
from custom_components.pyscript.global_ctx import GlobalContextMgr
@@ -33,15 +34,8 @@ async def setup_script(hass, notify_q, now, source):
3334
), patch(
3435
"homeassistant.config.load_yaml_config_file", return_value={}
3536
), patch(
36-
"custom_components.pyscript.load_all_requirement_lines",
37-
return_value={
38-
"/some/config/dir/pyscript/requirements.txt": [
39-
"pytube==9.7.0\n",
40-
"# another test comment\n",
41-
"pykakasi==2.0.1 # test comment\n",
42-
"\n",
43-
]
44-
},
37+
"custom_components.pyscript.process_all_requirements",
38+
return_value={"/some/config/dir/pyscript/requirements.txt": ["pytube==2.0.1", "pykakasi==2.0.1"]},
4539
):
4640
assert await async_setup_component(hass, "pyscript", {DOMAIN: {}})
4741

@@ -457,14 +451,9 @@ def func5(var_name=None, value=None):
457451
), patch(
458452
"homeassistant.config.load_yaml_config_file", return_value={}
459453
), patch(
460-
"custom_components.pyscript.load_all_requirement_lines",
454+
"custom_components.pyscript.process_all_requirements",
461455
return_value={
462-
"/some/config/dir/pyscript/requirements.txt": [
463-
"pytube==9.7.0\n",
464-
"# another test comment\n",
465-
"pykakasi==2.0.1 # test comment\n",
466-
"\n",
467-
]
456+
"/some/config/dir/pyscript/requirements.txt": ["pytube==2.0.1", "pykakasi==2.0.1"]
468457
},
469458
):
470459
reload_param = {}
@@ -511,10 +500,52 @@ async def test_install_requirements(hass):
511500
) as install_requirements:
512501
await setup_script(hass, None, dt(2020, 7, 1, 11, 59, 59, 999999), "")
513502
assert install_requirements.called
514-
assert install_requirements.call_args[0][2] == ["pytube==9.7.0", "pykakasi==2.0.1"]
503+
assert install_requirements.call_args[0][2] == ["pytube==2.0.1", "pykakasi==2.0.1"]
515504
install_requirements.reset_mock()
516505
# Because in tests, packages are not installed, we fake that they are
517506
# installed so we can test that we don't attempt to install them
518-
with patch("custom_components.pyscript.installed_version", return_value="2.0.1"):
507+
with patch("custom_components.pyscript.installed_version", return_value="2.0.1"), patch(
508+
"custom_components.pyscript.process_all_requirements",
509+
return_value={"/some/config/dir/pyscript/requirements.txt": []},
510+
):
519511
await hass.services.async_call("pyscript", "reload", {}, blocking=True)
520512
assert not install_requirements.called
513+
514+
515+
async def test_process_requirements(hass, caplog):
516+
"""Test process requirements function."""
517+
requirements = [
518+
"/some/config/dir/pyscript/requirements.txt",
519+
]
520+
521+
source = """
522+
# Comment
523+
pytube==2.0.1
524+
>
525+
pkakasi==2.0.1 # Comment
526+
527+
528+
"""
529+
530+
with patch("custom_components.pyscript.glob.iglob", return_value=requirements), patch(
531+
"custom_components.pyscript.open", mock_open(read_data=source), create=True
532+
):
533+
all_requirements = await hass.async_add_executor_job(
534+
process_all_requirements, hass, REQUIREMENTS_PATHS, REQUIREMENTS_FILE
535+
)
536+
assert requirements[0] in all_requirements
537+
assert all_requirements[requirements[0]] == ["pytube==2.0.1", "pkakasi==2.0.1"]
538+
539+
with patch("custom_components.pyscript.installed_version") as installed_version:
540+
installed_version.side_effect = ["2.0.1", "1.0.0"]
541+
all_requirements = await hass.async_add_executor_job(
542+
process_all_requirements, hass, REQUIREMENTS_PATHS, REQUIREMENTS_FILE
543+
)
544+
assert requirements[0] in all_requirements
545+
assert all_requirements[requirements[0]] == []
546+
547+
assert caplog.record_tuples[0] == (
548+
"custom_components.pyscript",
549+
30,
550+
"`pkakasi` already found but found version `1.0.0` does not match requirement. Keeping found version.",
551+
)

tests/test_jupyter.py

+2-9
Original file line numberDiff line numberDiff line change
@@ -118,15 +118,8 @@ async def setup_script(hass, now, source, no_connect=False):
118118
), patch(
119119
"homeassistant.config.load_yaml_config_file", return_value={}
120120
), patch(
121-
"custom_components.pyscript.load_all_requirement_lines",
122-
return_value={
123-
"/some/config/dir/pyscript/requirements.txt": [
124-
"pytube==9.7.0\n",
125-
"# another test comment\n",
126-
"pykakasi==2.0.1 # test comment\n",
127-
"\n",
128-
]
129-
},
121+
"custom_components.pyscript.process_all_requirements",
122+
return_value={"/some/config/dir/pyscript/requirements.txt": ["pytube==2.0.1", "pykakasi==2.0.1"]},
130123
):
131124
assert await async_setup_component(hass, "pyscript", {DOMAIN: {}})
132125

tests/test_unique.py

+2-9
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,8 @@ async def setup_script(hass, notify_q, now, source):
2424
), patch(
2525
"homeassistant.config.load_yaml_config_file", return_value={}
2626
), patch(
27-
"custom_components.pyscript.load_all_requirement_lines",
28-
return_value={
29-
"/some/config/dir/pyscript/requirements.txt": [
30-
"pytube==9.7.0\n",
31-
"# another test comment\n",
32-
"pykakasi==2.0.1 # test comment\n",
33-
"\n",
34-
]
35-
},
27+
"custom_components.pyscript.process_all_requirements",
28+
return_value={"/some/config/dir/pyscript/requirements.txt": ["pytube==2.0.1", "pykakasi==2.0.1"]},
3629
):
3730
assert await async_setup_component(hass, "pyscript", {DOMAIN: {}})
3831

0 commit comments

Comments
 (0)