Coverage for src/iso_freeze/sync.py: 67%
24 statements
« prev ^ index » next coverage.py v6.4.2, created at 2022-08-01 11:38 +0200
« prev ^ index » next coverage.py v6.4.2, created at 2022-08-01 11:38 +0200
1"""Sync environment with pip install --report output."""
3import json
4from pathlib import Path
5from typing import Optional
7from iso_freeze.lib import PyPackage, run_pip
10def sync(requirements: list[PyPackage], python_exec: Path) -> None:
11 """Sync environment with pip install --report output.
13 Arguments:
14 requirements -- List of dependencies to sync with (list[PyPackage])
15 python_exec -- Path to Python interpreter to use (Path)
16 """
17 installed_packages: list[PyPackage] = get_installed_packages(
18 pip_list_output=get_pip_list_output(python_exec=python_exec)
19 )
20 additional_packages: Optional[list[str]] = get_additional_packages(
21 installed_packages=installed_packages,
22 to_install=requirements,
23 )
24 if additional_packages:
25 remove_additional_packages(
26 additional_packages=additional_packages,
27 python_exec=python_exec,
28 )
29 install_pip_report_output(
30 to_install=format_package_list(packages=requirements),
31 python_exec=python_exec,
32 )
35def get_pip_list_output(python_exec: Path) -> list[dict[str, str]]:
36 """Run pip list --format json output.
38 Arguments:
39 python_exec -- Path to Python interpreter to use (Path)
41 Returns:
42 Pip list output as JSON (list[dict[str, str]])
43 """
44 return json.loads(
45 run_pip(
46 command=[
47 python_exec,
48 "-m",
49 "pip",
50 "list",
51 "--format",
52 "json",
53 "--exclude-editable",
54 ],
55 check_output=True,
56 )
57 )
60def get_installed_packages(pip_list_output: list[dict[str, str]]) -> list[PyPackage]:
61 """Return pip list output as PyPackage objects.
63 Arguments:
64 pip_list_output -- Pip list output as JSON (list[dict[str, str]])
66 Returns:
67 List of packages from current environment (list[PyPackage])
68 """
69 return [
70 PyPackage(name=package["name"], version=package["version"])
71 for package in pip_list_output
72 ]
75def get_additional_packages(
76 installed_packages: list[PyPackage], to_install: list[PyPackage]
77) -> Optional[list[str]]:
78 """Filter out pip packages installed in current environment but not in pip report.
80 Arguments:
81 installed_packages -- List of packages installed in current environment
82 (list[PyPackage])
83 to_install -- List of packages taken from pip install --report
84 (list[PyPackages])
86 Returns:
87 List of installed packages not in pip report (Optional[list[str]])
88 """
89 # Create two lists with packages names only for easy comparison
90 installed_names_only: list[str] = [package.name for package in installed_packages]
91 to_install_names_only: list[str] = [package.name for package in to_install]
92 return [
93 package
94 for package in installed_names_only
95 if package not in to_install_names_only
96 # Don't remove default packages
97 if package not in ["pip", "setuptools"]
98 ]
101def remove_additional_packages(
102 additional_packages: Optional[list[str]], python_exec: Path
103) -> None:
104 """Remove installed packages not included in pip install --report.
106 Arguments:
107 additional_packages -- List of packages to remove (Optional[list[str]])
108 python_exec -- Path to Python executable (Path)
109 """
110 run_pip(
111 command=[python_exec, "-m", "pip", "uninstall", "-y", *additional_packages],
112 check_output=False,
113 )
116def format_package_list(packages: list[PyPackage]) -> list[str]:
117 """Create list in the format ["package1==versionX"] to pass that to `pip install`.
119 Arguments:
120 packages -- List of packages to install (list[PyPackage])
122 Returns:
123 List of packages to be passed to pip install (list[str])
124 """
125 return [f"{package.name}=={package.version}" for package in packages]
128def install_pip_report_output(to_install: list[str], python_exec: Path) -> None:
129 """Install packages with pinned versions from pip install --report output.
131 Arguments:
132 to_install -- List of packages taken from pip install --report (list[str])
133 python_exec -- Path to Python executable (Path)
134 """
135 run_pip(
136 command=[python_exec, "-m", "pip", "install", "--upgrade", *to_install],
137 check_output=False,
138 )