gpu-stack-inspector 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. gpu_stack_inspector/__init__.py +8 -0
  2. gpu_stack_inspector/__main__.py +6 -0
  3. gpu_stack_inspector/cli.py +335 -0
  4. gpu_stack_inspector/collectors/__init__.py +1 -0
  5. gpu_stack_inspector/collectors/environment.py +31 -0
  6. gpu_stack_inspector/collectors/host.py +48 -0
  7. gpu_stack_inspector/collectors/nvidia.py +166 -0
  8. gpu_stack_inspector/collectors/python_env.py +63 -0
  9. gpu_stack_inspector/collectors/toolkit.py +160 -0
  10. gpu_stack_inspector/compatibility/__init__.py +1 -0
  11. gpu_stack_inspector/compatibility/data.json +24 -0
  12. gpu_stack_inspector/compatibility/loader.py +75 -0
  13. gpu_stack_inspector/compatibility/versions.py +25 -0
  14. gpu_stack_inspector/exit_codes.py +22 -0
  15. gpu_stack_inspector/models.py +349 -0
  16. gpu_stack_inspector/orchestrator.py +132 -0
  17. gpu_stack_inspector/privacy.py +237 -0
  18. gpu_stack_inspector/probes/__init__.py +1 -0
  19. gpu_stack_inspector/probes/metadata_probe.py +230 -0
  20. gpu_stack_inspector/probes/metadata_program.py +232 -0
  21. gpu_stack_inspector/probes/runtime_probe.py +153 -0
  22. gpu_stack_inspector/probes/runtime_program.py +101 -0
  23. gpu_stack_inspector/reports/__init__.py +1 -0
  24. gpu_stack_inspector/reports/json_report.py +43 -0
  25. gpu_stack_inspector/reports/markdown.py +182 -0
  26. gpu_stack_inspector/reports/terminal.py +298 -0
  27. gpu_stack_inspector/rules/__init__.py +1 -0
  28. gpu_stack_inspector/rules/catalog.py +21 -0
  29. gpu_stack_inspector/rules/comparison.py +73 -0
  30. gpu_stack_inspector/rules/compatibility.py +256 -0
  31. gpu_stack_inspector/rules/differences.py +64 -0
  32. gpu_stack_inspector/rules/engine.py +101 -0
  33. gpu_stack_inspector/rules/host.py +259 -0
  34. gpu_stack_inspector/rules/interpreters.py +422 -0
  35. gpu_stack_inspector/rules/torch_build.py +227 -0
  36. gpu_stack_inspector/runner.py +273 -0
  37. gpu_stack_inspector-0.1.0.dist-info/METADATA +134 -0
  38. gpu_stack_inspector-0.1.0.dist-info/RECORD +41 -0
  39. gpu_stack_inspector-0.1.0.dist-info/WHEEL +4 -0
  40. gpu_stack_inspector-0.1.0.dist-info/entry_points.txt +3 -0
  41. gpu_stack_inspector-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,273 @@
1
+ """Bounded, shell-free execution of diagnostic commands.
2
+
3
+ Every external command the tool runs goes through a :class:`CommandRunner`, so that
4
+ collectors can be tested with a fake one and the real one can enforce the same limits
5
+ everywhere: an argument list (never a shell), a timeout, an output cap and no stdin.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import contextlib
11
+ import os
12
+ import signal
13
+ import subprocess
14
+ import sys
15
+ import threading
16
+ import time
17
+ from collections.abc import Mapping, Sequence
18
+ from dataclasses import dataclass
19
+ from enum import StrEnum
20
+ from pathlib import Path
21
+ from typing import IO, Protocol
22
+
23
+ from gpu_stack_inspector.models import FailureKind, ProbeFailure
24
+
25
+ DEFAULT_MAX_OUTPUT = 256 * 1024
26
+ """Bytes kept per stream; diagnostic output larger than this is truncated."""
27
+
28
+ _REAP_TIMEOUT = 2.0
29
+ """Seconds to wait for a finished command's pipes to close before giving up on them."""
30
+
31
+ _CHUNK = 64 * 1024
32
+ _DETAIL_LIMIT = 200
33
+
34
+
35
+ class Outcome(StrEnum):
36
+ """How a command ended, as far as the runner can tell."""
37
+
38
+ OK = "ok"
39
+ NONZERO = "nonzero"
40
+ TIMEOUT = "timeout"
41
+ NOT_FOUND = "not_found"
42
+ PERMISSION = "permission"
43
+ OS_ERROR = "os_error"
44
+
45
+
46
+ @dataclass(frozen=True, slots=True)
47
+ class CommandResult:
48
+ """What a command printed and how it ended.
49
+
50
+ ``returncode`` is ``None`` when the command never started or was killed after a
51
+ timeout. ``error`` holds the operating system's message when it never started.
52
+ """
53
+
54
+ argv: tuple[str, ...]
55
+ outcome: Outcome
56
+ returncode: int | None = None
57
+ stdout: str = ""
58
+ stderr: str = ""
59
+ stdout_truncated: bool = False
60
+ stderr_truncated: bool = False
61
+ duration: float = 0.0
62
+ error: str = ""
63
+
64
+ @property
65
+ def ok(self) -> bool:
66
+ return self.outcome is Outcome.OK
67
+
68
+
69
+ class CommandRunner(Protocol):
70
+ """Runs one command and reports the result; never raises for command failures."""
71
+
72
+ def run(
73
+ self,
74
+ argv: Sequence[str],
75
+ *,
76
+ timeout: float,
77
+ env_overrides: Mapping[str, str] | None = None,
78
+ cwd: Path | None = None,
79
+ ) -> CommandResult: ...
80
+
81
+
82
+ _FAILURE_KINDS = {
83
+ Outcome.NOT_FOUND: FailureKind.NOT_FOUND,
84
+ Outcome.PERMISSION: FailureKind.PERMISSION_DENIED,
85
+ Outcome.TIMEOUT: FailureKind.TIMEOUT,
86
+ Outcome.NONZERO: FailureKind.NONZERO_EXIT,
87
+ Outcome.OS_ERROR: FailureKind.OS_ERROR,
88
+ }
89
+
90
+
91
+ def probe_failure(probe: str, result: CommandResult) -> ProbeFailure | None:
92
+ """Describe why ``result`` gave no usable output, or ``None`` if it succeeded.
93
+
94
+ The detail is short and meant for humans; it is redacted with the rest of the
95
+ snapshot before any output is written.
96
+ """
97
+ if result.ok:
98
+ return None
99
+ program = result.argv[0]
100
+ if result.outcome is Outcome.NOT_FOUND:
101
+ detail = f"{program} was not found"
102
+ elif result.outcome is Outcome.PERMISSION:
103
+ detail = f"permission denied when starting {program}"
104
+ elif result.outcome is Outcome.TIMEOUT:
105
+ detail = f"{program} did not finish within {result.duration:.1f} s and was stopped"
106
+ elif result.outcome is Outcome.NONZERO:
107
+ detail = f"{program} exited with code {result.returncode}"
108
+ # Some tools (nvidia-smi among them) print their errors on stdout.
109
+ first_line = _first_line(result.stderr) or _first_line(result.stdout)
110
+ if first_line:
111
+ detail += f": {first_line}"
112
+ else:
113
+ detail = f"{program} could not be started: {result.error}"
114
+ if len(detail) > _DETAIL_LIMIT:
115
+ detail = detail[: _DETAIL_LIMIT - 1] + "…"
116
+ return ProbeFailure(probe=probe, kind=_FAILURE_KINDS[result.outcome], detail=detail)
117
+
118
+
119
+ def _first_line(text: str) -> str:
120
+ return next((line.strip() for line in text.splitlines() if line.strip()), "")
121
+
122
+
123
+ class SubprocessRunner:
124
+ """The real runner: ``subprocess`` with an argument list, never a shell.
125
+
126
+ Each command runs in its own session (on POSIX), so a timeout or Ctrl+C kills the
127
+ command together with any children it started. Windows only kills the command itself.
128
+ """
129
+
130
+ def __init__(self, max_output: int = DEFAULT_MAX_OUTPUT) -> None:
131
+ if max_output <= 0:
132
+ raise ValueError("max_output must be positive")
133
+ self.max_output = max_output
134
+
135
+ def run(
136
+ self,
137
+ argv: Sequence[str],
138
+ *,
139
+ timeout: float,
140
+ env_overrides: Mapping[str, str] | None = None,
141
+ cwd: Path | None = None,
142
+ ) -> CommandResult:
143
+ args = _checked_argv(argv)
144
+ if timeout <= 0:
145
+ raise ValueError("timeout must be positive")
146
+ env = {**os.environ, **env_overrides} if env_overrides else None
147
+ started = time.monotonic()
148
+ try:
149
+ proc = subprocess.Popen( # noqa: S603 - argument list without a shell
150
+ args,
151
+ stdin=subprocess.DEVNULL,
152
+ stdout=subprocess.PIPE,
153
+ stderr=subprocess.PIPE,
154
+ env=env,
155
+ cwd=cwd,
156
+ start_new_session=sys.platform != "win32",
157
+ )
158
+ except FileNotFoundError as exc:
159
+ return _not_started(args, Outcome.NOT_FOUND, exc, started)
160
+ except PermissionError as exc:
161
+ return _not_started(args, Outcome.PERMISSION, exc, started)
162
+ except OSError as exc:
163
+ return _not_started(args, Outcome.OS_ERROR, exc, started)
164
+
165
+ if proc.stdout is None or proc.stderr is None: # cannot happen with PIPE
166
+ raise RuntimeError("subprocess pipes were not created")
167
+ readers = (
168
+ _CappedReader(proc.stdout, self.max_output),
169
+ _CappedReader(proc.stderr, self.max_output),
170
+ )
171
+ for reader in readers:
172
+ reader.start()
173
+ timed_out = False
174
+ try:
175
+ try:
176
+ proc.wait(timeout=timeout)
177
+ except subprocess.TimeoutExpired:
178
+ timed_out = True
179
+ _kill(proc)
180
+ proc.wait()
181
+ # A child that escaped the process group may hold a pipe open; don't wait
182
+ # for it forever, keep what was read so far.
183
+ deadline = time.monotonic() + _REAP_TIMEOUT
184
+ for reader in readers:
185
+ reader.join(max(0.0, deadline - time.monotonic()))
186
+ except BaseException:
187
+ # Ctrl+C or an internal error: never leave the command running behind us,
188
+ # and reap it so it does not linger as a zombie.
189
+ _kill(proc)
190
+ with contextlib.suppress(subprocess.TimeoutExpired):
191
+ proc.wait(timeout=_REAP_TIMEOUT)
192
+ raise
193
+
194
+ out, err = readers
195
+ return CommandResult(
196
+ argv=args,
197
+ outcome=Outcome.TIMEOUT
198
+ if timed_out
199
+ else (Outcome.OK if proc.returncode == 0 else Outcome.NONZERO),
200
+ returncode=None if timed_out else proc.returncode,
201
+ stdout=_decode(out.data),
202
+ stderr=_decode(err.data),
203
+ stdout_truncated=out.truncated,
204
+ stderr_truncated=err.truncated,
205
+ duration=time.monotonic() - started,
206
+ )
207
+
208
+
209
+ class _CappedReader(threading.Thread):
210
+ """Drains one pipe, keeping at most ``limit`` bytes.
211
+
212
+ It keeps reading (and discarding) past the limit so the command never blocks on a
213
+ full pipe.
214
+ """
215
+
216
+ def __init__(self, stream: IO[bytes], limit: int) -> None:
217
+ super().__init__(daemon=True)
218
+ self._stream = stream
219
+ self._limit = limit
220
+ self._kept = bytearray()
221
+ self.truncated = False
222
+
223
+ def run(self) -> None:
224
+ # os.read returns as soon as any bytes are available; a buffered read() would
225
+ # wait for a full chunk and lose the output of a command whose child keeps the
226
+ # pipe open.
227
+ fd = self._stream.fileno()
228
+ with contextlib.suppress(OSError, ValueError), self._stream:
229
+ while chunk := os.read(fd, _CHUNK):
230
+ room = self._limit - len(self._kept)
231
+ if room > 0:
232
+ self._kept += chunk[:room]
233
+ if len(chunk) > room:
234
+ self.truncated = True
235
+
236
+ @property
237
+ def data(self) -> bytes:
238
+ return bytes(self._kept)
239
+
240
+
241
+ def _checked_argv(argv: Sequence[str]) -> tuple[str, ...]:
242
+ if isinstance(argv, str):
243
+ raise TypeError("argv must be a sequence of str, not a single command string")
244
+ args = tuple(argv)
245
+ if not args:
246
+ raise ValueError("argv is empty")
247
+ for arg in args:
248
+ if not isinstance(arg, str):
249
+ raise TypeError(f"argv items must be str, got {type(arg).__name__}")
250
+ return args
251
+
252
+
253
+ def _not_started(
254
+ args: tuple[str, ...], outcome: Outcome, exc: OSError, started: float
255
+ ) -> CommandResult:
256
+ return CommandResult(
257
+ argv=args,
258
+ outcome=outcome,
259
+ error=exc.strerror or str(exc),
260
+ duration=time.monotonic() - started,
261
+ )
262
+
263
+
264
+ def _kill(proc: subprocess.Popen[bytes]) -> None:
265
+ if sys.platform == "win32":
266
+ proc.kill()
267
+ return
268
+ with contextlib.suppress(ProcessLookupError, PermissionError): # already gone
269
+ os.killpg(proc.pid, signal.SIGKILL)
270
+
271
+
272
+ def _decode(data: bytes) -> str:
273
+ return data.decode("utf-8", errors="replace")
@@ -0,0 +1,134 @@
1
+ Metadata-Version: 2.4
2
+ Name: gpu-stack-inspector
3
+ Version: 0.1.0
4
+ Summary: Explain why your AI code uses the GPU in one Python environment but not another.
5
+ Keywords: cuda,pytorch,gpu,nvidia,conda,venv,diagnostics,troubleshooting
6
+ Author: BhuwanJoshi-01
7
+ Author-email: BhuwanJoshi-01 <bhuwanj779@gmail.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Operating System :: POSIX :: Linux
15
+ Classifier: Operating System :: Microsoft :: Windows
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
22
+ Classifier: Topic :: System :: Hardware
23
+ Classifier: Topic :: Utilities
24
+ Classifier: Typing :: Typed
25
+ Requires-Dist: rich>=15.0.0
26
+ Requires-Dist: typer>=0.27.3
27
+ Requires-Python: >=3.11
28
+ Project-URL: Homepage, https://bhuwanjoshi-01.github.io/gpu-stack-inspector/
29
+ Project-URL: Documentation, https://github.com/BhuwanJoshi-01/gpu-stack-inspector/blob/main/docs/troubleshooting.md
30
+ Project-URL: Source, https://github.com/BhuwanJoshi-01/gpu-stack-inspector
31
+ Project-URL: Issues, https://github.com/BhuwanJoshi-01/gpu-stack-inspector/issues
32
+ Project-URL: Changelog, https://github.com/BhuwanJoshi-01/gpu-stack-inspector/blob/main/CHANGELOG.md
33
+ Description-Content-Type: text/markdown
34
+
35
+ # GPU Stack Inspector
36
+
37
+ **Explain why your AI code uses the GPU in one Python environment but not another.**
38
+
39
+ [![CI](https://github.com/BhuwanJoshi-01/gpu-stack-inspector/actions/workflows/ci.yml/badge.svg)](https://github.com/BhuwanJoshi-01/gpu-stack-inspector/actions/workflows/ci.yml)
40
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/BhuwanJoshi-01/gpu-stack-inspector/blob/main/LICENSE)
41
+ [![Website](https://img.shields.io/badge/website-gpu--stack--inspector-17202e.svg)](https://bhuwanjoshi-01.github.io/gpu-stack-inspector/)
42
+
43
+ `nvidia-smi` shows your GPU, yet `torch.cuda.is_available()` is `True` in one environment and `False` in another.
44
+ GPU Stack Inspector inspects several Python interpreters from one command, shows what differs between them, and
45
+ explains which difference matters, with the evidence:
46
+
47
+ ![compare on a laptop with an RTX 3050: a conda environment with torch 2.11.0+cu128 against a venv with torch 2.15.0+cpu. The table shows the torch build CUDA 12.8 versus CPU only and fifteen nvidia CUDA wheels missing from the venv; the findings fail GSI-TORCH-002 for the CPU-only build and warn GSI-DIFF-001 that the environments have different PyTorch builds.](https://raw.githubusercontent.com/BhuwanJoshi-01/gpu-stack-inspector/main/docs/images/compare.svg)
48
+
49
+ > **Status:** 0.1.0 is the first release, validated on one laptop with five real environments. Reports from
50
+ > other machines are very welcome.
51
+
52
+ ## Install
53
+
54
+ The tool needs Python 3.11 or newer; the interpreters it inspects can be Python 3.8 or newer.
55
+
56
+ ```bash
57
+ uv tool install git+https://github.com/BhuwanJoshi-01/gpu-stack-inspector # until the PyPI release
58
+ pipx install git+https://github.com/BhuwanJoshi-01/gpu-stack-inspector # or with pipx
59
+ ```
60
+
61
+ ## Use
62
+
63
+ ```bash
64
+ gpu-stack-inspector compare --python ~/envs/works/bin/python --python ~/envs/broken/bin/python
65
+ gpu-stack-inspector inspect --python ~/envs/broken/bin/python # one environment in detail
66
+ gpu-stack-inspector probe --runtime --python ~/envs/broken/bin/python # opt-in: really runs a tiny CUDA operation
67
+ gpu-stack-inspector report --python ~/envs/broken/bin/python --output gpu-report.md # redacted, for an issue
68
+ gpu-stack-inspector rules list # every check; `rules explain CODE` for one
69
+ ```
70
+
71
+ Without `--python` it inspects the `python` your shell runs. See the [command reference](https://github.com/BhuwanJoshi-01/gpu-stack-inspector/blob/main/docs/cli.md).
72
+
73
+ ## What it checks
74
+
75
+ - **The machine**: driver tools, whether the driver answers and which CUDA versions it supports, the GPUs and their
76
+ compute capability, `CUDA_VISIBLE_DEVICES`.
77
+ - **Each interpreter**: whether it starts, whether it is the one your shell runs, whether torch is installed where it
78
+ is needed, and which build it is (CUDA with its version, CPU-only, ROCm), decided from the version tag,
79
+ `torch/version.py`, the CUDA libraries in `torch/lib`, the `nvidia-*` requirements and conda's records, without
80
+ importing torch.
81
+ - **Compatibility**: the driver against the CUDA version torch was built for and against nvcc, using dated,
82
+ source-tagged NVIDIA compatibility data.
83
+ - **The comparison**: every field that differs, and whether the environments have different PyTorch builds.
84
+ - **At runtime** (`probe --runtime` only): importing torch and running an operation on the GPU, step by step.
85
+
86
+ Every finding has a stable code, the evidence it rests on, what to do next and the command to check it afterwards.
87
+ [Troubleshooting](https://github.com/BhuwanJoshi-01/gpu-stack-inspector/blob/main/docs/troubleshooting.md) explains the common ones.
88
+
89
+ ## What it promises
90
+
91
+ - **Read-only.** It never installs, upgrades or changes anything, and never needs `sudo`. It writes nothing except a
92
+ report file you name.
93
+ - **Your environments stay untouched.** A fixed probe program reads each interpreter's metadata. torch is only
94
+ imported with `probe --runtime`, because creating a CUDA context uses GPU memory and can hang on a broken system.
95
+ - **Unknown is not a pass.** A check that could not run is unknown or not tested, never green.
96
+ - **Shareable output.** Home paths, user and host names, secrets and tokens are redacted before anything is printed
97
+ or written, JSON included, and each report says what was redacted. No network access, no telemetry.
98
+
99
+ Details in [privacy](https://github.com/BhuwanJoshi-01/gpu-stack-inspector/blob/main/docs/privacy.md).
100
+
101
+ ## Tested on
102
+
103
+ An Ubuntu 26.04 laptop with an RTX 3050 (driver 595.99.02) and five real environments: conda and uv, Python 3.8 to
104
+ 3.13, CUDA 11.3 to 13.0 builds and a CPU-only build. CI runs the tests on Linux and Windows and the probe in Python
105
+ 3.8 to 3.10 and PyPy. Windows and WSL with a GPU, containers and macOS are **not validated**; see the
106
+ [support matrix](https://github.com/BhuwanJoshi-01/gpu-stack-inspector/blob/main/docs/support-matrix.md).
107
+
108
+ ## Alternatives
109
+
110
+ If you only need to check a single environment, these may already be enough:
111
+
112
+ - [cuda-doctor](https://pypi.org/project/cuda-doctor/) checks the GPU, driver, toolkit, compiler and PyTorch of the
113
+ environment it is installed in.
114
+ - [env-doctor](https://github.com/mitulgarg/env-doctor) checks the driver, CUDA, cuDNN and frameworks, and compares
115
+ machines in a fleet dashboard.
116
+ - [cudadoc](https://www.cudadoc.com/docs/quickstart) includes a GPU validation suite.
117
+
118
+ GPU Stack Inspector focuses on comparing **environments on the same machine**.
119
+
120
+ ## Documentation
121
+
122
+ [Command reference](https://github.com/BhuwanJoshi-01/gpu-stack-inspector/blob/main/docs/cli.md) · [Troubleshooting](https://github.com/BhuwanJoshi-01/gpu-stack-inspector/blob/main/docs/troubleshooting.md) · [Privacy](https://github.com/BhuwanJoshi-01/gpu-stack-inspector/blob/main/docs/privacy.md) ·
123
+ [Support matrix](https://github.com/BhuwanJoshi-01/gpu-stack-inspector/blob/main/docs/support-matrix.md) · [JSON output](https://github.com/BhuwanJoshi-01/gpu-stack-inspector/blob/main/docs/json-output.md) · [Architecture](https://github.com/BhuwanJoshi-01/gpu-stack-inspector/blob/main/docs/architecture.md) ·
124
+ [Sample reports](https://github.com/BhuwanJoshi-01/gpu-stack-inspector/tree/main/docs/sample-reports) · [Roadmap](https://github.com/BhuwanJoshi-01/gpu-stack-inspector/blob/main/docs/roadmap.md)
125
+
126
+ ## Contributing
127
+
128
+ Test reports, fixtures from unusual systems, bug reports and pull requests are welcome; see
129
+ [CONTRIBUTING.md](https://github.com/BhuwanJoshi-01/gpu-stack-inspector/blob/main/CONTRIBUTING.md). Security problems, including a report that leaks private data, go through
130
+ [SECURITY.md](https://github.com/BhuwanJoshi-01/gpu-stack-inspector/blob/main/SECURITY.md).
131
+
132
+ ## License
133
+
134
+ [MIT](https://github.com/BhuwanJoshi-01/gpu-stack-inspector/blob/main/LICENSE)
@@ -0,0 +1,41 @@
1
+ gpu_stack_inspector/__init__.py,sha256=dJXcPa3gAk1Lr3JjLxEZHQ9pS80p6Y7ylewqH0okWMU,312
2
+ gpu_stack_inspector/__main__.py,sha256=tmc5ufsSfSmgDN2QuJOsq8RlW70WjKaz1gFINtSd4Ug,128
3
+ gpu_stack_inspector/cli.py,sha256=_D5XO1-7v8q7PnKVrSWgfeQ31dNeo8sCbpeXFF3ISug,11627
4
+ gpu_stack_inspector/collectors/__init__.py,sha256=UvHX8m7P7cdXpAqQoW8ZoY5h3A2G3u3ilKxbFtOdHe8,82
5
+ gpu_stack_inspector/collectors/environment.py,sha256=yKeNL2eGTA9W4qLPG6SU8lmBfUQM4wdO4yGxGdm9fSw,964
6
+ gpu_stack_inspector/collectors/host.py,sha256=Duub9aC848bijkFUWgdjMkhVV_N9ofBot-dBtOwIO8c,1529
7
+ gpu_stack_inspector/collectors/nvidia.py,sha256=dRwbY3-4oKxtsjc76W8HVkJFFMGw83OJjvy525YSXIo,6474
8
+ gpu_stack_inspector/collectors/python_env.py,sha256=f2TI9feAhLv3qOMS1gkeasSHifxZ-uvUIyjg7mumLD8,2445
9
+ gpu_stack_inspector/collectors/toolkit.py,sha256=UblkT0BMMbHQHzOcENeldrHHVBaBxBqSW5SqQEiSekM,6033
10
+ gpu_stack_inspector/compatibility/__init__.py,sha256=TJYv_PUowuJROTrGnI06F40l0UGntQtDfxqf6D4uxXk,66
11
+ gpu_stack_inspector/compatibility/data.json,sha256=zhPNBcwwBECbsYFO0P_JQLYAuJgeiDLwcG02AEodLpA,1061
12
+ gpu_stack_inspector/compatibility/loader.py,sha256=w1rmv6P5o0DYhzPGPqpUWHyHtWEPMfzky6UzuSnal_Q,2490
13
+ gpu_stack_inspector/compatibility/versions.py,sha256=X05dB9ktYeIAHCW7ocnb9R9OjTzolUFQYHpilxG9qzY,834
14
+ gpu_stack_inspector/exit_codes.py,sha256=Ho_9iLzwyhdg2X2gysPa07QhlZaSYET7HBJJT43W_F0,624
15
+ gpu_stack_inspector/models.py,sha256=AikJcsR9urr7pVkCcnTLUQlytyzeS2egUzghwMsHOcA,11139
16
+ gpu_stack_inspector/orchestrator.py,sha256=BwA4GmyB8FPXBjD4GdnRuYl47CR9edSvOXc1pX9c0Bc,4906
17
+ gpu_stack_inspector/privacy.py,sha256=HX-PxSRlCrpbOKQZhfriaI9ew8YLhUmsr_-SRAW0JGs,9617
18
+ gpu_stack_inspector/probes/__init__.py,sha256=frWsRtpoGKb9mbZ2PaMHWWQd705IsB_55isbjJPrI6E,82
19
+ gpu_stack_inspector/probes/metadata_probe.py,sha256=zAyonIfxsMLuGL2yjR-HBu2RXAhTNwHX5iyRIMIfIAw,9034
20
+ gpu_stack_inspector/probes/metadata_program.py,sha256=XWlbw5Rf2aZig3OuBYMcCrlM5T-LhBGt1f2qbFlo3cs,8626
21
+ gpu_stack_inspector/probes/runtime_probe.py,sha256=0cWM1tTMdEqWPyRNPdr8At-Byl1COFCZTE4kU08OPVc,5793
22
+ gpu_stack_inspector/probes/runtime_program.py,sha256=xofX8l4rrcJeB5Vsv6H97lUch9Jh5izo-KkZn3ke8Oo,3642
23
+ gpu_stack_inspector/reports/__init__.py,sha256=6iGY7rFTa5ktGL0b0TGufqpyHRiWWr3BpfEg1SKJ1bY,89
24
+ gpu_stack_inspector/reports/json_report.py,sha256=duYjE2qViu7xNX1HNjnkMjkv-DED_mAHbvl-Br4-MTw,1730
25
+ gpu_stack_inspector/reports/markdown.py,sha256=74rcrvfLPTpJtoK-romtoYehlJy-ev9S2MmejUXCbWQ,7472
26
+ gpu_stack_inspector/reports/terminal.py,sha256=XClXXDOfYTZ8fPS5FLUqUVbGa4AZHCI91DQSjuL6sb4,11330
27
+ gpu_stack_inspector/rules/__init__.py,sha256=fD9lbPmNoP91YhOhDY9xYicy1lmaX-nPtT05J-fpSSc,95
28
+ gpu_stack_inspector/rules/catalog.py,sha256=5c1co1lmEKrZC_uycEtVuzBYTMNgnMapg0RxPr_HIBY,705
29
+ gpu_stack_inspector/rules/comparison.py,sha256=5nsECvfWBElFpjmblobvByzQUmyjPtwZtqKzM-pXbr0,2892
30
+ gpu_stack_inspector/rules/compatibility.py,sha256=jgApZOh4rRtvq28bJ5Jveex1YGLldGM0OIrv3F-Cjug,10268
31
+ gpu_stack_inspector/rules/differences.py,sha256=z8qiHPXZICizaiRU39SGOafuReM60mQKifGQEWwq4Do,2594
32
+ gpu_stack_inspector/rules/engine.py,sha256=DOH8UVkatvW-YRwmdnQLuS07DBi5U8KH0HwzMYG_2q4,3582
33
+ gpu_stack_inspector/rules/host.py,sha256=tO8kccA7ufnKayACP2zDDLgrYZu-_Aw9b-0G6kbLZx0,10695
34
+ gpu_stack_inspector/rules/interpreters.py,sha256=kL1TloMD9AhdVkJK_1ShY-bPtj27mq95Zpsd6oh2Nkk,19567
35
+ gpu_stack_inspector/rules/torch_build.py,sha256=hjmZMc2D4LODDM63CxGuJNfPhY2z0wzI8aQiqTjxyIg,9348
36
+ gpu_stack_inspector/runner.py,sha256=ZT95pEgGacd3kWyjVXEvXU0WDP_eQl41XID7HrhyjK8,9204
37
+ gpu_stack_inspector-0.1.0.dist-info/licenses/LICENSE,sha256=qpnoghKC1sfa6tVZTac6xDaNwFgOvSqQIYH0s3szqFc,1071
38
+ gpu_stack_inspector-0.1.0.dist-info/WHEEL,sha256=MWx7psoF7KNST9G72pGtiBYuaXRXQXBk9pw5R6FzYko,80
39
+ gpu_stack_inspector-0.1.0.dist-info/entry_points.txt,sha256=G__M6nBl-XGQjLr_CkHrjTyOTbrTibNu4cpf122PjJQ,70
40
+ gpu_stack_inspector-0.1.0.dist-info/METADATA,sha256=bXLePLnBTa5nIt1Z7suHPNBMqUM76OxqN9gFJCPTtAI,8389
41
+ gpu_stack_inspector-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.13.7
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ gpu-stack-inspector = gpu_stack_inspector.cli:main
3
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 BhuwanJoshi-01
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.