Coverage for muutils/sysinfo.py: 82%
79 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-28 17:24 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-28 17:24 +0000
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 # in python <= 3.9 `Distribution` has no attribute `name`
55 pckgs: list[tuple[str, str]] = [
56 (
57 (
58 x.metadata.get("Name", "<unknown>") # type: ignore[attr-defined]
59 if sys.version_info < (3, 10)
60 else x.name # type: ignore[attr-defined]
61 ),
62 x.version,
63 )
64 for x in distributions()
65 ]
66 return {
67 "n_packages": len(pckgs),
68 "packages": pckgs,
69 }
71 @staticmethod
72 def pytorch() -> dict:
73 """pytorch and cuda information"""
74 try:
75 import torch
76 import torch.version
77 except Exception as e:
78 return {
79 "importable": False,
80 "error": str(e),
81 }
83 output: dict = {"importable": True}
85 output["torch.__version__"] = torch.__version__
86 output["torch.version.cuda"] = torch.version.cuda
87 output["torch.version.debug"] = torch.version.debug
88 output["torch.version.git_version"] = torch.version.git_version
89 output["torch.version.hip"] = torch.version.hip
90 output["torch.cuda.is_available()"] = torch.cuda.is_available()
91 output["torch.cuda.device_count()"] = torch.cuda.device_count()
92 output["torch.cuda.is_initialized()"] = torch.cuda.is_initialized()
94 if torch.cuda.is_available():
95 import os
97 cuda_version_nvcc: str = os.popen("nvcc --version").read()
98 output["nvcc --version"] = cuda_version_nvcc.split("\n")
100 if torch.cuda.device_count() > 0:
101 n_devices: int = torch.cuda.device_count()
102 output["torch.cuda.current_device()"] = torch.cuda.current_device()
103 output["torch devices"] = []
104 for current_device in range(n_devices):
105 try:
106 # print(f'checking current device {current_device} of {torch.cuda.device_count()} devices')
107 # print(f'\tdevice {current_device}')
108 # dev_prop = torch.cuda.get_device_properties(torch.device(0))
109 # print(f'\t name: {dev_prop.name}')
110 # print(f'\t version: {dev_prop.major}.{dev_prop.minor}')
111 # print(f'\t total_memory: {dev_prop.total_memory}')
112 # print(f'\t multi_processor_count: {dev_prop.multi_processor_count}')
113 # print(f'\t')
114 dev_prop = torch.cuda.get_device_properties(current_device)
115 output["torch devices"].append(
116 {
117 "device": current_device,
118 "name": dev_prop.name,
119 "version": {
120 "major": dev_prop.major,
121 "minor": dev_prop.minor,
122 },
123 "total_memory": dev_prop.total_memory,
124 "multi_processor_count": dev_prop.multi_processor_count,
125 }
126 )
127 except Exception as e:
128 output["torch devices"].append(
129 {
130 "device": current_device,
131 "error": str(e),
132 }
133 )
134 return output
136 @staticmethod
137 def platform() -> dict:
138 import platform
140 items = [
141 "platform",
142 "machine",
143 "processor",
144 "system",
145 "version",
146 "architecture",
147 "uname",
148 "node",
149 "python_branch",
150 "python_build",
151 "python_compiler",
152 "python_implementation",
153 ]
155 return {x: getattr(platform, x)() for x in items}
157 @staticmethod
158 def git_info(with_log: bool = False) -> dict:
159 git_version: dict = _popen(["git", "version"])
160 git_status: dict = _popen(["git", "status"])
161 if not git_status["stderr"] or git_status["stderr"].startswith(
162 "fatal: not a git repository"
163 ):
164 return {
165 "git version": git_version["stdout"],
166 "git status": git_status,
167 }
168 else:
169 output: dict = {
170 "git version": git_version["stdout"],
171 "git status": git_status,
172 "git branch": _popen(["git", "branch"], split_out=True),
173 "git remote -v": _popen(["git", "remote", "-v"], split_out=True),
174 }
175 if with_log:
176 output["git log"] = _popen(["git", "log"], split_out=False)
178 return output
180 @classmethod
181 def get_all(
182 cls,
183 include: typing.Optional[tuple[str, ...]] = None,
184 exclude: tuple[str, ...] = tuple(),
185 ) -> dict:
186 include_meta: tuple[str, ...]
187 if include is None:
188 include_meta = tuple(cls.__dict__.keys())
189 else:
190 include_meta = include
192 return {
193 x: getattr(cls, x)()
194 for x in include_meta
195 if all(
196 [
197 not x.startswith("_"),
198 x not in exclude,
199 callable(getattr(cls, x)),
200 x != "get_all",
201 x in include if include is not None else True,
202 ]
203 )
204 }
207if __name__ == "__main__":
208 import pprint
210 pprint.pprint(SysInfo.get_all())