Coverage for pipxl/sync.py: 100%

44 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2022-11-28 21:48 +0100

1# SPDX-FileCopyrightText: 2022-present Jeroen van Zundert <mail@jeroenvanzundert.nl> 

2# 

3# SPDX-License-Identifier: MIT 

4 

5from __future__ import annotations 

6 

7import textwrap 

8from pathlib import Path 

9 

10import typer 

11 

12from pipxl.pip_cli import pip_cli 

13from pipxl.resolver import pip_resolve 

14 

15pipxl_deps = ["typer", "click", "pytest", "pytest-cov", "coverage"] 

16PACKAGES_TO_IGNORE = ["pip", "pipxl", *pipxl_deps] 

17 

18 

19def sync(files_in: list[Path] | None = None, package_spec: list[str] | None = None, dry_run: bool = False) -> None: 

20 reqs, _ = pip_resolve(files_in, package_spec) 

21 target_install = [r.name + "==" + r.version for r in reqs] 

22 

23 installed = get_installed_packages() 

24 

25 to_install, to_uninstall = merge(target_install, installed) 

26 

27 if dry_run: 

28 typer.echo("Would uninstall:") 

29 typer.echo(textwrap.indent("\n".join(to_uninstall), prefix="\t")) 

30 typer.echo("Would install") 

31 typer.echo(textwrap.indent("\n".join(to_install), prefix="\t")) 

32 else: 

33 uninstall_packages(to_uninstall) 

34 install_packages(to_install) 

35 

36 

37def get_installed_packages() -> list[str]: 

38 cmd = ["list", "--format=freeze"] 

39 output = pip_cli(cmd).stdout 

40 assert isinstance(output, str) 

41 return output.splitlines() 

42 

43 

44def install_packages(targets: list[str]) -> None: 

45 cmd = ["install"] + targets 

46 pip_cli(cmd) 

47 

48 

49def uninstall_packages(targets: list[str]) -> None: 

50 cmd = ["uninstall", "-y"] + targets 

51 pip_cli(cmd) 

52 

53 

54def merge(target_install: list[str], installed: list[str]) -> tuple[list[str], list[str]]: 

55 to_uninstall = set() 

56 to_install = set() 

57 

58 for pkg in installed: 

59 if pkg in target_install: 

60 continue 

61 

62 if "==" in pkg and pkg.split("==")[0] in PACKAGES_TO_IGNORE: 

63 continue 

64 

65 to_uninstall.add(pkg) 

66 

67 for pkg in target_install: 

68 if pkg not in installed: 

69 to_install.add(pkg) 

70 

71 return (sorted(list(to_install)), sorted(list(to_uninstall)))