/ src / bugreport / _internal / debug.py
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()