Coverage for muutils/sysinfo.py: 82%
79 statements
« prev ^ index » next coverage.py v7.6.1, created at 2025-05-30 22:10 -0600
« prev ^ index » next coverage.py v7.6.1, created at 2025-05-30 22:10 -0600
1"utilities for getting information about the system, see `SysInfo` class"
3from __future__ import annotations
5import subprocess
6import sys
7import typing
8from importlib.metadata import distributions
11def _popen(cmd: list[str], split_out: bool = False) -> dict[str, typing.Any]:
12 p: subprocess.Popen = subprocess.Popen(
13 cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE
14 )
16 stdout, stderr = p.communicate()
18 p_out: typing.Union[str, list[str], None]
19 if stdout:
20 p_out = stdout.decode("utf-8")
21 if split_out:
22 assert isinstance(p_out, str)
23 p_out = p_out.strip().split("\n")
24 else:
25 p_out = None
27 return {
28 "stdout": p_out,
29 "stderr": stderr.decode("utf-8") if stderr else None,
30 "returncode": p.returncode if p.returncode is None else int(p.returncode),
31 }
34class SysInfo:
35 """getters for various information about the system"""
37 @staticmethod
38 def python() -> dict:
39 """details about python version"""
40 ver_tup = sys.version_info
41 return {
42 "version": sys.version,
43 "version_info": ver_tup,
44 "major": ver_tup[0],
45 "minor": ver_tup[1],
46 "micro": ver_tup[2],
47 "releaselevel": ver_tup[3],
48 "serial": ver_tup[4],
49 }
51 @staticmethod
52 def pip() -> dict:
53 """installed packages info"""
54 # for some reason, python 3.8 thinks `Distribution` has no attribute `name`?
55 pckgs: list[tuple[str, str]] = [(x.name, x.version) for x in distributions()] # type: ignore[attr-defined]
56 return {
57 "n_packages": len(pckgs),
58 "packages": pckgs,
59 }
61 @staticmethod
62 def pytorch() -> dict:
63 """pytorch and cuda information"""
64 try:
65 import torch
66 import torch.version
67 except Exception as e:
68 return {
69 "importable": False,
70 "error": str(e),
71 }
73 output: dict = {"importable": True}
75 output["torch.__version__"] = torch.__version__
76 output["torch.version.cuda"] = torch.version.cuda
77 output["torch.version.debug"] = torch.version.debug
78 output["torch.version.git_version"] = torch.version.git_version
79 output["torch.version.hip"] = torch.version.hip
80 output["torch.cuda.is_available()"] = torch.cuda.is_available()
81 output["torch.cuda.device_count()"] = torch.cuda.device_count()
82 output["torch.cuda.is_initialized()"] = torch.cuda.is_initialized()
84 if torch.cuda.is_available():
85 import os
87 cuda_version_nvcc: str = os.popen("nvcc --version").read()
88 output["nvcc --version"] = cuda_version_nvcc.split("\n")
90 if torch.cuda.device_count() > 0:
91 n_devices: int = torch.cuda.device_count()
92 output["torch.cuda.current_device()"] = torch.cuda.current_device()
93 output["torch devices"] = []
94 for current_device in range(n_devices):
95 try:
96 # print(f'checking current device {current_device} of {torch.cuda.device_count()} devices')
97 # print(f'\tdevice {current_device}')
98 # dev_prop = torch.cuda.get_device_properties(torch.device(0))
99 # print(f'\t name: {dev_prop.name}')
100 # print(f'\t version: {dev_prop.major}.{dev_prop.minor}')
101 # print(f'\t total_memory: {dev_prop.total_memory}')
102 # print(f'\t multi_processor_count: {dev_prop.multi_processor_count}')
103 # print(f'\t')
104 dev_prop = torch.cuda.get_device_properties(current_device)
105 output["torch devices"].append(
106 {
107 "device": current_device,
108 "name": dev_prop.name,
109 "version": {
110 "major": dev_prop.major,
111 "minor": dev_prop.minor,
112 },
113 "total_memory": dev_prop.total_memory,
114 "multi_processor_count": dev_prop.multi_processor_count,
115 }
116 )
117 except Exception as e:
118 output["torch devices"].append(
119 {
120 "device": current_device,
121 "error": str(e),
122 }
123 )
124 return output
126 @staticmethod
127 def platform() -> dict:
128 import platform
130 items = [
131 "platform",
132 "machine",
133 "processor",
134 "system",
135 "version",
136 "architecture",
137 "uname",
138 "node",
139 "python_branch",
140 "python_build",
141 "python_compiler",
142 "python_implementation",
143 ]
145 return {x: getattr(platform, x)() for x in items}
147 @staticmethod
148 def git_info(with_log: bool = False) -> dict:
149 git_version: dict = _popen(["git", "version"])
150 git_status: dict = _popen(["git", "status"])
151 if not git_status["stderr"] or git_status["stderr"].startswith(
152 "fatal: not a git repository"
153 ):
154 return {
155 "git version": git_version["stdout"],
156 "git status": git_status,
157 }
158 else:
159 output: dict = {
160 "git version": git_version["stdout"],
161 "git status": git_status,
162 "git branch": _popen(["git", "branch"], split_out=True),
163 "git remote -v": _popen(["git", "remote", "-v"], split_out=True),
164 }
165 if with_log:
166 output["git log"] = _popen(["git", "log"], split_out=False)
168 return output
170 @classmethod
171 def get_all(
172 cls,
173 include: typing.Optional[tuple[str, ...]] = None,
174 exclude: tuple[str, ...] = tuple(),
175 ) -> dict:
176 include_meta: tuple[str, ...]
177 if include is None:
178 include_meta = tuple(cls.__dict__.keys())
179 else:
180 include_meta = include
182 return {
183 x: getattr(cls, x)()
184 for x in include_meta
185 if all(
186 [
187 not x.startswith("_"),
188 x not in exclude,
189 callable(getattr(cls, x)),
190 x != "get_all",
191 x in include if include is not None else True,
192 ]
193 )
194 }
197if __name__ == "__main__":
198 import pprint
200 pprint.pprint(SysInfo.get_all())