gpustack-runtime 0.2.0.post4__tar.gz → 0.2.1__tar.gz

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 (131) hide show
  1. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/.gitignore +1 -0
  2. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/PKG-INFO +1 -1
  3. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/_version.py +2 -2
  4. gpustack_runtime-0.2.1/gpustack_runtime/_version_appendix.py +1 -0
  5. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/detector/__utils__.py +32 -0
  6. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/detector/ascend.py +1 -1
  7. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/detector/iluvatar.py +8 -7
  8. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/detector/pyamdsmi/__init__.py +6 -1
  9. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/detector/pydcmi/__init__.py +6 -1
  10. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/detector/pyhgml/__init__.py +6 -1
  11. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/detector/pyixml/__init__.py +6 -1
  12. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/detector/pymtml/__init__.py +6 -1
  13. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/detector/pymxsml/__init__.py +12 -2
  14. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/detector/pynvml/__init__.py +6 -1
  15. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/detector/pyrocmsmi/__init__.py +6 -1
  16. gpustack_runtime-0.2.1/tests/gpustack_runtime/detector/test_detector_utils.py +170 -0
  17. gpustack_runtime-0.2.0.post4/gpustack_runtime/_version_appendix.py +0 -1
  18. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/.codespelldict +0 -0
  19. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/.codespellrc +0 -0
  20. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/.dockerignore +0 -0
  21. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/.gitattributes +0 -0
  22. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/.pre-commit-config.yaml +0 -0
  23. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/.python-version +0 -0
  24. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/LICENSE +0 -0
  25. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/Makefile +0 -0
  26. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/README.md +0 -0
  27. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/deploy/manifests/docker-compose.yaml +0 -0
  28. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/deploy/manifests/kubernetes.yaml +0 -0
  29. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/docs/index.md +0 -0
  30. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/docs/modules/gpustack_runtime.deployer.md +0 -0
  31. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/docs/modules/gpustack_runtime.detector.md +0 -0
  32. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/docs/modules/gpustack_runtime.md +0 -0
  33. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/__init__.py +0 -0
  34. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/__main__.py +0 -0
  35. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/_version.pyi +0 -0
  36. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/cmds/__init__.py +0 -0
  37. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/cmds/__types__.py +0 -0
  38. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/cmds/deployer.py +0 -0
  39. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/cmds/detector.py +0 -0
  40. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/cmds/images.py +0 -0
  41. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/deployer/__init__.py +0 -0
  42. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/deployer/__patches__.py +0 -0
  43. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/deployer/__types__.py +0 -0
  44. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/deployer/__utils__.py +0 -0
  45. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/deployer/cdi/__init__.py +0 -0
  46. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/deployer/cdi/__types__.py +0 -0
  47. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/deployer/cdi/__utils__.py +0 -0
  48. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/deployer/cdi/amd.py +0 -0
  49. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/deployer/cdi/ascend.py +0 -0
  50. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/deployer/cdi/hygon.py +0 -0
  51. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/deployer/cdi/iluvatar.py +0 -0
  52. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/deployer/cdi/metax.py +0 -0
  53. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/deployer/cdi/thead.py +0 -0
  54. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/deployer/docker.py +0 -0
  55. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/deployer/k8s/devicemanager/__init__.py +0 -0
  56. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/deployer/kuberentes.py +0 -0
  57. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/deployer/podman.py +0 -0
  58. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/detector/__init__.py +0 -0
  59. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/detector/__types__.py +0 -0
  60. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/detector/amd.py +0 -0
  61. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/detector/cambricon.py +0 -0
  62. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/detector/hygon.py +0 -0
  63. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/detector/metax.py +0 -0
  64. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/detector/mthreads.py +0 -0
  65. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/detector/nvidia.py +0 -0
  66. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/detector/pyamdgpu/__init__.py +0 -0
  67. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/detector/pyhgml/libhgml.so +0 -0
  68. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/detector/pyhgml/libuki.so +0 -0
  69. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/detector/pyhsa/__init__.py +0 -0
  70. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/detector/thead.py +0 -0
  71. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/envs.py +0 -0
  72. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/gpustack_runtime/logging.py +0 -0
  73. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/hatch.toml +0 -0
  74. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/mkdocs.yml +0 -0
  75. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/pack/Dockerfile +0 -0
  76. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/pack/Dockerfile.dummy +0 -0
  77. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/pyproject.toml +0 -0
  78. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/pytest.ini +0 -0
  79. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/ruff.toml +0 -0
  80. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/deployer/fixtures/__init__.py +0 -0
  81. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/deployer/fixtures/test_compare_versions.json +0 -0
  82. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/deployer/fixtures/test_correct_runner_image.json +0 -0
  83. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/deployer/fixtures/test_load_yaml_or_json.json +0 -0
  84. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/deployer/fixtures/test_load_yaml_or_json_multiple_jsons.json +0 -0
  85. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/deployer/fixtures/test_load_yaml_or_json_multiple_yamls.yaml +0 -0
  86. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/deployer/fixtures/test_load_yaml_or_json_single_json.json +0 -0
  87. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/deployer/fixtures/test_load_yaml_or_json_single_yaml.yaml +0 -0
  88. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/deployer/fixtures/test_nginx_entrypoint.sh +0 -0
  89. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/deployer/test_utils.py +0 -0
  90. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/fixtures/__init__.py +0 -0
  91. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/samples/README.md +0 -0
  92. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/samples/detect_output_amd_mi300x.json +0 -0
  93. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/samples/detect_output_amd_mi308x.json +0 -0
  94. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/samples/detect_output_amd_rx7800xt.json +0 -0
  95. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/samples/detect_output_ascend_310p3.json +0 -0
  96. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/samples/detect_output_ascend_910b2.json +0 -0
  97. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/samples/detect_output_hygon_k100ai.json +0 -0
  98. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/samples/detect_output_metax_c500.json +0 -0
  99. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_gb10.json +0 -0
  100. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_h100.json +0 -0
  101. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_h100_mig.json +0 -0
  102. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_h200.json +0 -0
  103. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_rtx4080super.json +0 -0
  104. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_rtx4090d.json +0 -0
  105. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_rtx5090d.json +0 -0
  106. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/samples/detect_output_thead_ppu.json +0 -0
  107. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/samples/topology_output_amd_mi300x.json +0 -0
  108. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/samples/topology_output_amd_mi308x.json +0 -0
  109. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/samples/topology_output_amd_rx7800xt.json +0 -0
  110. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/samples/topology_output_ascend_310p3.json +0 -0
  111. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/samples/topology_output_ascend_910b2.json +0 -0
  112. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/samples/topology_output_hygon_k100ai.json +0 -0
  113. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/samples/topology_output_metax_c500.json +0 -0
  114. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/samples/topology_output_mthreads_s5000.json +0 -0
  115. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_h100.json +0 -0
  116. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_h100_mig.json +0 -0
  117. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_h200.json +0 -0
  118. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_rtx4080super.json +0 -0
  119. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_rtx4090d.json +0 -0
  120. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_rtx5090d.json +0 -0
  121. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/samples/topology_output_thead_ppu.json +0 -0
  122. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/test_amd.py +0 -0
  123. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/test_ascend.py +0 -0
  124. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/test_cambricon.py +0 -0
  125. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/test_hygon.py +0 -0
  126. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/test_iluvatar.py +0 -0
  127. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/test_metax.py +0 -0
  128. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/test_mthreads.py +0 -0
  129. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/tests/gpustack_runtime/detector/test_nvidia.py +0 -0
  130. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/uv.lock +0 -0
  131. {gpustack_runtime-0.2.0.post4 → gpustack_runtime-0.2.1}/uv.toml +0 -0
@@ -3,6 +3,7 @@
3
3
  .vscode/
4
4
  .DS_Store
5
5
  *.swp
6
+ .claude/
6
7
 
7
8
  # Python-generated files
8
9
  _version.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gpustack-runtime
3
- Version: 0.2.0.post4
3
+ Version: 0.2.1
4
4
  Summary: GPUStack Runtime is library for detecting GPU resources and launching GPU workloads.
5
5
  Project-URL: Homepage, https://github.com/gpustack/runtime
6
6
  Project-URL: Bug Tracker, https://github.com/gpustack/gpustack/issues
@@ -27,8 +27,8 @@ version_tuple: VERSION_TUPLE
27
27
  __commit_id__: COMMIT_ID
28
28
  commit_id: COMMIT_ID
29
29
 
30
- __version__ = version = '0.2.0.post4'
31
- __version_tuple__ = version_tuple = (0, 2, 0, 'post4')
30
+ __version__ = version = '0.2.1'
31
+ __version_tuple__ = version_tuple = (0, 2, 1)
32
32
  try:
33
33
  from ._version_appendix import git_commit
34
34
  __commit_id__ = commit_id = git_commit
@@ -0,0 +1 @@
1
+ git_commit = "1d1e894"
@@ -16,6 +16,38 @@ from typing import Any
16
16
  from gpustack_runtime import envs
17
17
 
18
18
 
19
+ def clone_exception(exc: BaseException) -> BaseException:
20
+ """
21
+ Build a fresh copy of an exception, without its traceback or chaining state.
22
+
23
+ The detector library wrappers cache the init failure and re-raise it on every
24
+ subsequent call to fail fast. Re-raising the *same* object is a memory leak:
25
+ CPython appends a new frame to its ``__traceback__`` on each raise, and those
26
+ frames retain the caller's locals indefinitely (gpustack/gpustack#5342).
27
+
28
+ Cloning via ``BaseException.__new__`` preserves the original type and
29
+ attributes regardless of the class's constructor signature -- some binding
30
+ error types take an error code and leave ``args`` empty (e.g. amdsmi's
31
+ ``AmdSmiLibraryException``), so ``type(exc)(*exc.args)`` cannot reconstruct them.
32
+
33
+ Args:
34
+ exc:
35
+ The exception to clone.
36
+
37
+ Returns:
38
+ A fresh exception of the same type, carrying the same args and attributes
39
+ but no traceback or ``__cause__``/``__context__`` chain.
40
+
41
+ """
42
+ clone = BaseException.__new__(type(exc))
43
+ clone.args = exc.args
44
+ # Copy instance state (custom attributes, and __notes__ if any live here too).
45
+ # Guard against exotic exception types that don't expose __dict__.
46
+ if hasattr(exc, "__dict__"):
47
+ clone.__dict__.update(exc.__dict__)
48
+ return clone
49
+
50
+
19
51
  @dataclass
20
52
  class PCIDevice:
21
53
  address: str
@@ -658,5 +658,5 @@ def get_ascend_cann_variant(name: str | None) -> str | None:
658
658
  if version < 260:
659
659
  return "a3" # 910c/a3
660
660
  if version < 270:
661
- return "a5" # 910d/a5
661
+ return "950" # 950/a5
662
662
  return None
@@ -137,15 +137,16 @@ class IluvatarDetector(Detector):
137
137
  dev_mem = 0
138
138
  dev_mem_used = 0
139
139
  dev_mem_status = DeviceMemoryStatusEnum.HEALTHY
140
+ with contextlib.suppress(pyixml.NVMLError):
141
+ dev_mem_info = pyixml.nvmlDeviceGetMemoryInfo(dev)
142
+ dev_mem = byte_to_mebibyte( # byte to MiB
143
+ dev_mem_info.total,
144
+ )
145
+ dev_mem_used = byte_to_mebibyte( # byte to MiB
146
+ dev_mem_info.used,
147
+ )
140
148
  if not envs.GPUSTACK_RUNTIME_DETECT_NO_HEALTH_CHECK:
141
149
  with contextlib.suppress(pyixml.NVMLError):
142
- dev_mem_info = pyixml.nvmlDeviceGetMemoryInfo(dev)
143
- dev_mem = byte_to_mebibyte( # byte to MiB
144
- dev_mem_info.total,
145
- )
146
- dev_mem_used = byte_to_mebibyte( # byte to MiB
147
- dev_mem_info.used,
148
- )
149
150
  dev_health = pyixml.ixmlDeviceGetHealth(dev)
150
151
  if dev_health != pyixml.IXML_HEALTH_OK:
151
152
  dev_mem_status = DeviceMemoryStatusEnum.UNHEALTHY
@@ -32,7 +32,12 @@ try:
32
32
 
33
33
  if _libInitialized:
34
34
  if _libInitializedException is not None:
35
- raise _libInitializedException
35
+ # Re-raise a fresh copy: re-raising the same cached exception object
36
+ # appends a traceback frame on every call, and those frames retain the
37
+ # caller's locals, leaking memory over time. See gpustack/gpustack#5342.
38
+ from ..__utils__ import clone_exception
39
+
40
+ raise clone_exception(_libInitializedException) from None
36
41
  return
37
42
 
38
43
  try:
@@ -780,7 +780,12 @@ def dcmi_init():
780
780
 
781
781
  if _libInitialized:
782
782
  if _libInitializedException is not None:
783
- raise _libInitializedException
783
+ # Re-raise a fresh copy: re-raising the same cached exception object
784
+ # appends a traceback frame on every call, and those frames retain the
785
+ # caller's locals, leaking memory over time. See gpustack/gpustack#5342.
786
+ from ..__utils__ import clone_exception
787
+
788
+ raise clone_exception(_libInitializedException) from None
784
789
  return
785
790
 
786
791
  try:
@@ -2380,7 +2380,12 @@ def hgmlInitWithFlags(flags):
2380
2380
 
2381
2381
  if _libInitialized:
2382
2382
  if _libInitializedException is not None:
2383
- raise _libInitializedException
2383
+ # Re-raise a fresh copy: re-raising the same cached exception object
2384
+ # appends a traceback frame on every call, and those frames retain the
2385
+ # caller's locals, leaking memory over time. See gpustack/gpustack#5342.
2386
+ from ..__utils__ import clone_exception
2387
+
2388
+ raise clone_exception(_libInitializedException) from None
2384
2389
  return
2385
2390
 
2386
2391
  try:
@@ -2131,7 +2131,12 @@ def nvmlInitWithFlags(flags):
2131
2131
 
2132
2132
  if _libInitialized:
2133
2133
  if _libInitializedException is not None:
2134
- raise _libInitializedException
2134
+ # Re-raise a fresh copy: re-raising the same cached exception object
2135
+ # appends a traceback frame on every call, and those frames retain the
2136
+ # caller's locals, leaking memory over time. See gpustack/gpustack#5342.
2137
+ from ..__utils__ import clone_exception
2138
+
2139
+ raise clone_exception(_libInitializedException) from None
2135
2140
  return
2136
2141
 
2137
2142
  try:
@@ -15,7 +15,12 @@ def mtmlLibraryInit():
15
15
 
16
16
  if _libInitialized:
17
17
  if _libInitializedException is not None:
18
- raise _libInitializedException
18
+ # Re-raise a fresh copy: re-raising the same cached exception object
19
+ # appends a traceback frame on every call, and those frames retain the
20
+ # caller's locals, leaking memory over time. See gpustack/gpustack#5342.
21
+ from ..__utils__ import clone_exception
22
+
23
+ raise clone_exception(_libInitializedException) from None
19
24
  return
20
25
 
21
26
  try:
@@ -778,7 +778,12 @@ def mxSmlInit():
778
778
 
779
779
  if _libInitialized:
780
780
  if _libInitializedException is not None:
781
- raise _libInitializedException
781
+ # Re-raise a fresh copy: re-raising the same cached exception object
782
+ # appends a traceback frame on every call, and those frames retain the
783
+ # caller's locals, leaking memory over time. See gpustack/gpustack#5342.
784
+ from ..__utils__ import clone_exception
785
+
786
+ raise clone_exception(_libInitializedException) from None
782
787
  return
783
788
 
784
789
  try:
@@ -802,7 +807,12 @@ def mxSmlInitWithFlags(flags):
802
807
 
803
808
  if _libInitialized:
804
809
  if _libInitializedException is not None:
805
- raise _libInitializedException
810
+ # Re-raise a fresh copy: re-raising the same cached exception object
811
+ # appends a traceback frame on every call, and those frames retain the
812
+ # caller's locals, leaking memory over time. See gpustack/gpustack#5342.
813
+ from ..__utils__ import clone_exception
814
+
815
+ raise clone_exception(_libInitializedException) from None
806
816
  return
807
817
 
808
818
  try:
@@ -16,7 +16,12 @@ def nvmlInitWithFlags(flags):
16
16
 
17
17
  if _libInitialized:
18
18
  if _libInitializedException is not None:
19
- raise _libInitializedException
19
+ # Re-raise a fresh copy: re-raising the same cached exception object
20
+ # appends a traceback frame on every call, and those frames retain the
21
+ # caller's locals, leaking memory over time. See gpustack/gpustack#5342.
22
+ from ..__utils__ import clone_exception
23
+
24
+ raise clone_exception(_libInitializedException) from None
20
25
  return
21
26
 
22
27
  try:
@@ -180,7 +180,12 @@ def rsmi_init(flags=0):
180
180
 
181
181
  if _libInitialized:
182
182
  if _libInitializedException is not None:
183
- raise _libInitializedException
183
+ # Re-raise a fresh copy: re-raising the same cached exception object
184
+ # appends a traceback frame on every call, and those frames retain the
185
+ # caller's locals, leaking memory over time. See gpustack/gpustack#5342.
186
+ from ..__utils__ import clone_exception
187
+
188
+ raise clone_exception(_libInitializedException) from None
184
189
  return
185
190
 
186
191
  try:
@@ -0,0 +1,170 @@
1
+ from __future__ import annotations
2
+
3
+ import sys
4
+ from typing import ClassVar
5
+
6
+ import pytest
7
+
8
+ from gpustack_runtime.detector.__utils__ import clone_exception
9
+
10
+
11
+ # --------------------------------------------------------------------------- #
12
+ # Exception classes that mimic the real binding error types, to exercise the #
13
+ # constructor shapes clone_exception has to survive. #
14
+ # --------------------------------------------------------------------------- #
15
+ class RocmLikeError(Exception):
16
+ """Custom __init__ that does NOT call super().__init__ (like ROCMSMIError)."""
17
+
18
+ def __init__(self, value):
19
+ self.value = value
20
+
21
+ def __str__(self):
22
+ return f"rocm {self.value}"
23
+
24
+
25
+ class DcmiLikeError(Exception):
26
+ """Custom __new__(cls, value) that requires a positional arg (like DCMIError)."""
27
+
28
+ _mapping: ClassVar[dict] = {}
29
+
30
+ def __new__(cls, value):
31
+ target = DcmiLikeError._mapping.get(value, cls)
32
+ obj = Exception.__new__(target)
33
+ obj.value = value
34
+ return obj
35
+
36
+ def __str__(self):
37
+ return f"dcmi {self.value}"
38
+
39
+
40
+ class AmdSmiLikeError(Exception):
41
+ """super().__init__() with no args -> args == (); state lives in __dict__.
42
+
43
+ This is the case that `type(exc)(*exc.args)` CANNOT reconstruct.
44
+ """
45
+
46
+ def __init__(self, err_code):
47
+ super().__init__()
48
+ self.err_code = err_code
49
+ self.err_info = f"info-{err_code}"
50
+
51
+ def __str__(self):
52
+ return f"Error code: {self.err_code} | {self.err_info}"
53
+
54
+
55
+ def _tb_len(exc: BaseException) -> int:
56
+ n = 0
57
+ tb = exc.__traceback__
58
+ while tb:
59
+ n += 1
60
+ tb = tb.tb_next
61
+ return n
62
+
63
+
64
+ ALL_CASES = [
65
+ RocmLikeError(-99997),
66
+ DcmiLikeError(-8007),
67
+ AmdSmiLikeError(43),
68
+ ValueError("boom"),
69
+ RuntimeError("multi", "arg", 3),
70
+ ]
71
+
72
+
73
+ # --------------------------------------------------------------------------- #
74
+ # Correctness: the clone must be usable in place of the original. #
75
+ # --------------------------------------------------------------------------- #
76
+ def test_clone_preserves_type():
77
+ for exc in ALL_CASES:
78
+ clone = clone_exception(exc)
79
+ assert type(clone) is type(exc)
80
+
81
+
82
+ def test_clone_is_a_distinct_object():
83
+ for exc in ALL_CASES:
84
+ clone = clone_exception(exc)
85
+ assert clone is not exc
86
+
87
+
88
+ def test_clone_preserves_args_and_attributes_and_str():
89
+ for exc in ALL_CASES:
90
+ clone = clone_exception(exc)
91
+ assert clone.args == exc.args
92
+ assert clone.__dict__ == exc.__dict__
93
+ # str() relies on the preserved state; it must render identically.
94
+ assert str(clone) == str(exc)
95
+
96
+
97
+ def test_clone_still_matches_except_clause():
98
+ # A caller doing `except RocmLikeError` must still catch the clone.
99
+ with pytest.raises(RocmLikeError) as info:
100
+ raise clone_exception(RocmLikeError(-1))
101
+ assert info.value.value == -1
102
+
103
+
104
+ @pytest.mark.skipif(
105
+ sys.version_info < (3, 11),
106
+ reason="Exception.add_note() was added in Python 3.11",
107
+ )
108
+ def test_clone_preserves_notes():
109
+ # __notes__ (add_note, 3.11+) lives in __dict__, so it is carried over by
110
+ # the __dict__ copy without any special handling.
111
+ exc = ValueError("boom")
112
+ exc.add_note("first note")
113
+ exc.add_note("second note")
114
+ clone = clone_exception(exc)
115
+ assert clone.__notes__ == ["first note", "second note"]
116
+
117
+
118
+ def test_clone_handles_amdsmi_style_empty_args():
119
+ # The whole reason we don't use `type(exc)(*exc.args)`: it blows up here.
120
+ exc = AmdSmiLikeError(7)
121
+ assert exc.args == ()
122
+ clone = clone_exception(exc)
123
+ assert clone.err_code == 7
124
+ assert clone.err_info == "info-7"
125
+
126
+
127
+ # --------------------------------------------------------------------------- #
128
+ # The point of the helper: cloning + re-raising must NOT grow the cached #
129
+ # exception's traceback (this is the gpustack/gpustack#5342 regression). #
130
+ # --------------------------------------------------------------------------- #
131
+ def test_reraising_clones_does_not_grow_cached_traceback():
132
+ # Seed a traceback on the "cached" exception, like a first failed init.
133
+ with pytest.raises(RocmLikeError) as info:
134
+ raise RocmLikeError(-99997)
135
+ cached = info.value
136
+
137
+ baseline = _tb_len(cached)
138
+
139
+ raised = []
140
+ for _ in range(500):
141
+ with pytest.raises(RocmLikeError) as info:
142
+ raise clone_exception(cached) from None
143
+ raised.append(info.value)
144
+
145
+ # The cached object's traceback is untouched by the re-raises.
146
+ assert _tb_len(cached) == baseline
147
+ # Every raise produced a fresh object, never the cached one.
148
+ assert all(r is not cached for r in raised)
149
+
150
+
151
+ def test_contrast_reraising_same_object_would_grow_traceback():
152
+ # Demonstrates the bug the helper fixes: re-raising the SAME object grows
153
+ # its traceback unbounded. (Sanity check that our test measures the right
154
+ # thing; not testing clone_exception here.)
155
+ with pytest.raises(RocmLikeError) as info:
156
+ raise RocmLikeError(-99997)
157
+ cached = info.value
158
+
159
+ before = _tb_len(cached)
160
+ for _ in range(50):
161
+ with pytest.raises(RocmLikeError):
162
+ raise cached
163
+ assert _tb_len(cached) > before
164
+
165
+
166
+ def test_clone_severs_cause_and_context_when_raised_from_none():
167
+ with pytest.raises(ValueError, match="x") as info:
168
+ raise clone_exception(ValueError("x")) from None
169
+ assert info.value.__cause__ is None
170
+ assert info.value.__suppress_context__ is True
@@ -1 +0,0 @@
1
- git_commit = "992585c"