Coverage for src/duty/debug.py: 91.78%

63 statements  

« prev     ^ index     » next       coverage.py v7.6.3, created at 2024-10-17 17:18 +0200

1"""Debugging utilities.""" 

2 

3from __future__ import annotations 

4 

5import os 

6import platform 

7import sys 

8from dataclasses import dataclass 

9from importlib import metadata 

10 

11 

12@dataclass 

13class Variable: 

14 """Dataclass describing an environment variable.""" 

15 

16 name: str 

17 """Variable name.""" 

18 value: str 

19 """Variable value.""" 

20 

21 

22@dataclass 

23class Package: 

24 """Dataclass describing a Python package.""" 

25 

26 name: str 

27 """Package name.""" 

28 version: str 

29 """Package version.""" 

30 

31 

32@dataclass 

33class Environment: 

34 """Dataclass to store environment information.""" 

35 

36 interpreter_name: str 

37 """Python interpreter name.""" 

38 interpreter_version: str 

39 """Python interpreter version.""" 

40 interpreter_path: str 

41 """Path to Python executable.""" 

42 platform: str 

43 """Operating System.""" 

44 packages: list[Package] 

45 """Installed packages.""" 

46 variables: list[Variable] 

47 """Environment variables.""" 

48 

49 

50def _interpreter_name_version() -> tuple[str, str]: 

51 if hasattr(sys, "implementation"): 51 ↛ 58line 51 didn't jump to line 58 because the condition on line 51 was always true

52 impl = sys.implementation.version 

53 version = f"{impl.major}.{impl.minor}.{impl.micro}" 

54 kind = impl.releaselevel 

55 if kind != "final": 

56 version += kind[0] + str(impl.serial) 

57 return sys.implementation.name, version 

58 return "", "0.0.0" 

59 

60 

61def get_version(dist: str = "duty") -> str: 

62 """Get version of the given distribution. 

63 

64 Parameters: 

65 dist: A distribution name. 

66 

67 Returns: 

68 A version number. 

69 """ 

70 try: 

71 return metadata.version(dist) 

72 except metadata.PackageNotFoundError: 

73 return "0.0.0" 

74 

75 

76def get_debug_info() -> Environment: 

77 """Get debug/environment information. 

78 

79 Returns: 

80 Environment information. 

81 """ 

82 py_name, py_version = _interpreter_name_version() 

83 packages = ["duty"] 

84 variables = ["PYTHONPATH", *[var for var in os.environ if var.startswith("DUTY")]] 

85 return Environment( 

86 interpreter_name=py_name, 

87 interpreter_version=py_version, 

88 interpreter_path=sys.executable, 

89 platform=platform.platform(), 

90 variables=[Variable(var, val) for var in variables if (val := os.getenv(var))], 

91 packages=[Package(pkg, get_version(pkg)) for pkg in packages], 

92 ) 

93 

94 

95def print_debug_info() -> None: 

96 """Print debug/environment information.""" 

97 info = get_debug_info() 

98 print(f"- __System__: {info.platform}") 

99 print(f"- __Python__: {info.interpreter_name} {info.interpreter_version} ({info.interpreter_path})") 

100 print("- __Environment variables__:") 

101 for var in info.variables: 

102 print(f" - `{var.name}`: `{var.value}`") 

103 print("- __Installed packages__:") 

104 for pkg in info.packages: 

105 print(f" - `{pkg.name}` v{pkg.version}") 

106 

107 

108if __name__ == "__main__": 108 ↛ 109line 108 didn't jump to line 109 because the condition on line 108 was never true

109 print_debug_info()