debug.py
1 from __future__ import annotations 2 3 import os 4 import platform 5 import sys 6 from dataclasses import dataclass 7 from importlib import metadata 8 9 10 @dataclass 11 class _Variable: 12 """Dataclass describing an environment variable.""" 13 14 name: str 15 """Variable name.""" 16 value: str 17 """Variable value.""" 18 19 20 @dataclass 21 class _Package: 22 """Dataclass describing a Python package.""" 23 24 name: str 25 """Package name.""" 26 version: str 27 """Package version.""" 28 29 30 @dataclass 31 class _Environment: 32 """Dataclass to store environment information.""" 33 34 interpreter_name: str 35 """Python interpreter name.""" 36 interpreter_version: str 37 """Python interpreter version.""" 38 interpreter_path: str 39 """Path to Python executable.""" 40 platform: str 41 """Operating System.""" 42 packages: list[_Package] 43 """Installed packages.""" 44 variables: list[_Variable] 45 """Environment variables.""" 46 47 48 def _interpreter_name_version() -> tuple[str, str]: 49 if hasattr(sys, "implementation"): 50 impl = sys.implementation.version 51 version = f"{impl.major}.{impl.minor}.{impl.micro}" 52 kind = impl.releaselevel 53 if kind != "final": 54 version += kind[0] + str(impl.serial) 55 return sys.implementation.name, version 56 return "", "0.0.0" 57 58 59 def _get_version(dist: str = "bugreport") -> str: 60 """Get version of the given distribution. 61 62 Parameters: 63 dist: A distribution name. 64 65 Returns: 66 A version number. 67 """ 68 try: 69 return metadata.version(dist) 70 except metadata.PackageNotFoundError: 71 return "0.0.0" 72 73 74 def _get_debug_info() -> _Environment: 75 """Get debug/environment information. 76 77 Returns: 78 Environment information. 79 """ 80 py_name, py_version = _interpreter_name_version() 81 packages = ["bugreport"] 82 variables = ["PYTHONPATH", *[var for var in os.environ if var.startswith("BUGREPORT")]] 83 return _Environment( 84 interpreter_name=py_name, 85 interpreter_version=py_version, 86 interpreter_path=sys.executable, 87 platform=platform.platform(), 88 variables=[_Variable(var, val) for var in variables if (val := os.getenv(var))], 89 packages=[_Package(pkg, _get_version(pkg)) for pkg in packages], 90 ) 91 92 93 def _print_debug_info() -> None: 94 """Print debug/environment information.""" 95 info = _get_debug_info() 96 print(f"- __System__: {info.platform}") 97 print(f"- __Python__: {info.interpreter_name} {info.interpreter_version} ({info.interpreter_path})") 98 print("- __Environment variables__:") 99 for var in info.variables: 100 print(f" - `{var.name}`: `{var.value}`") 101 print("- __Installed packages__:") 102 for pkg in info.packages: 103 print(f" - `{pkg.name}` v{pkg.version}") 104 105 106 if __name__ == "__main__": 107 _print_debug_info()