Coverage for pipxl/deptree.py: 98%

37 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2022-11-28 20:56 +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 

10from pipxl.data import ReqFileEntry 

11from pipxl.resolver import pip_resolve 

12 

13 

14def deptree(files_in: list[Path] | None = None, package_spec: list[str] | None = None) -> str: 

15 reqs, _ = pip_resolve(files_in, package_spec) 

16 

17 top_level = [req for req in reqs if req.requested] 

18 

19 output = "" 

20 for req in top_level: 

21 output += f"{req.name}=={req.version}\n" 

22 output += textwrap.indent(_deps_to_string(req.requires, reqs), prefix="\t") 

23 

24 return output 

25 

26 

27def deptreerev(files_in: list[Path] | None = None, package_spec: list[str] | None = None) -> str: 

28 reqs, _ = pip_resolve(files_in, package_spec) 

29 

30 output = "" 

31 for req in reqs: 

32 output += f"{req.name}=={req.version}\n" 

33 output += textwrap.indent(_reverse_deps_to_string(req.required_by, reqs), prefix="\t") 

34 return output 

35 

36 

37def _deps_to_string(deps: dict[str, str], reqs: list[ReqFileEntry]) -> str: 

38 output = "" 

39 for dep_name, dep_spec in deps.items(): 

40 try: 

41 info = next(t for t in reqs if t.name == dep_name) 

42 output += f"{dep_name}=={info.version} [{dep_spec}]\n" 

43 output += textwrap.indent(_deps_to_string(info.requires, reqs), prefix="\t") 

44 except StopIteration: 

45 # StopIteration is raised if the dependency is not found in reqs. 

46 # If a package is not in reqs, it means that it would not be installed by pip. 

47 # This is usually the case for dependencies that are only required on other Python versions 

48 # or platforms, but not on the specific combination this function is run. 

49 continue 

50 

51 return output 

52 

53 

54def _reverse_deps_to_string(rev_deps: dict[str, str], reqs: list[ReqFileEntry]) -> str: 

55 output = "" 

56 for dep_name, dep_spec in rev_deps.items(): 

57 info = next(t for t in reqs if t.name == dep_name) 57 ↛ exitline 57 didn't finish the generator expression on line 57

58 output += f"{dep_name}=={info.version} [{dep_spec}]\n" 

59 output += textwrap.indent(_reverse_deps_to_string(info.required_by, reqs), prefix="\t") 

60 return output