diff --git a/docs/utils.rst b/docs/utils.rst index 839dbe49..ba691faa 100644 --- a/docs/utils.rst +++ b/docs/utils.rst @@ -39,7 +39,7 @@ To inspect (for example) the total buffer usage: :members: -.. autoclass:: wgpu._diagnostics.Diagnostics +.. autoclass:: wgpu.DiagnosticsBase :members: diff --git a/tests/test_diagnostics.py b/tests/test_diagnostics.py index 013adc38..99ebe294 100644 --- a/tests/test_diagnostics.py +++ b/tests/test_diagnostics.py @@ -6,7 +6,7 @@ from wgpu import _diagnostics from wgpu._diagnostics import ( DiagnosticsRoot, - Diagnostics, + DiagnosticsBase, ObjectTracker, dict_to_text, int_repr, @@ -29,7 +29,7 @@ def __exit__(self, *args): _diagnostics.diagnostics = wgpu.diagnostics -class CustomDiagnostics(Diagnostics): +class CustomDiagnostics(DiagnosticsBase): def __init__(self, name): super().__init__(name) self.tracker = ObjectTracker() diff --git a/wgpu/__init__.py b/wgpu/__init__.py index f6ea1da0..ef8ef480 100644 --- a/wgpu/__init__.py +++ b/wgpu/__init__.py @@ -3,7 +3,7 @@ """ from ._coreutils import logger # noqa: F401,F403 -from ._diagnostics import diagnostics # noqa: F401,F403 +from ._diagnostics import diagnostics, DiagnosticsBase # noqa: F401,F403 from .flags import * # noqa: F401,F403 from .enums import * # noqa: F401,F403 from .classes import * # noqa: F401,F403 diff --git a/wgpu/_diagnostics.py b/wgpu/_diagnostics.py index e0e0c83d..6679384f 100644 --- a/wgpu/_diagnostics.py +++ b/wgpu/_diagnostics.py @@ -50,18 +50,21 @@ def print_report(self): print(self.get_report(), end="") -class Diagnostics: +class DiagnosticsBase: """Object that represents diagnostics on a specific topic. - This is a base class that must be subclassed to provide diagnostics - on a certain topic. Instantiating the class registers it with the - root diagnostics object. + This is a base class that must be subclassed to provide diagnostics on + a certain topic. Typically only ``get_dict()`` needs to be implemented. + Instantiating the class registers it with the root diagnostics object. """ def __init__(self, name): - diagnostics._register_diagnostics(name, self) + if not (isinstance(name, str) and name.isidentifier()): + raise ValueError( + "Diagnostics name must be an identifier (i.e. use underscore instead of spaces)." + ) self.name = name - self.object_counts = {} + diagnostics._register_diagnostics(name, self) def __repr__(self): return f"" @@ -263,6 +266,8 @@ def dict_to_table(d, header, header_offest=0): row.append("") elif isinstance(val, str): row.append(val) + elif isinstance(val, bool): + row.append("✓" if val else "-") elif isinstance(val, int): row.append(int_repr(val)) elif isinstance(val, float): @@ -285,26 +290,25 @@ def dict_to_table(d, header, header_offest=0): def int_repr(val): """Represent an integer using K and M suffixes.""" prefix = "-" if val < 0 else "" + suffix = "" val = abs(val) - if val >= 1_000_000_000: # >= 1G - s = str(val / 1_000_000_000) - suffix = "G" - elif val >= 1_000_000: # >= 1M - s = str(val / 1_000_000) - suffix = "M" - elif val >= 1_000: # >= 1K - s = str(val / 1_000) - suffix = "K" - else: - s = str(val) - suffix = "" - if "." in s: - s1, _, s2 = s.partition(".") + s = str(val) + tail = 3 * ((len(s) - 1) // 3) + if tail > 0: + s1, s2 = s[:-tail], s[-tail:] n_decimals = max(0, 3 - len(s1)) s = s1 if n_decimals: s2 += "000" s = s1 + "." + s2[:n_decimals] + if tail == 3: + suffix = "K" + elif tail == 6: + suffix = "M" + elif tail == 9: + suffix = "G" + else: + suffix = f"E{tail}" return prefix + s + suffix @@ -427,7 +431,7 @@ def int_repr(val): diagnostics = DiagnosticsRoot() -class SystemDiagnostics(Diagnostics): +class SystemDiagnostics(DiagnosticsBase): """Provides basic system info.""" def get_dict(self): @@ -439,7 +443,7 @@ def get_dict(self): } -class WgpuNativeInfoDiagnostics(Diagnostics): +class WgpuNativeInfoDiagnostics(DiagnosticsBase): """Provides metadata about the wgpu-native backend.""" def get_dict(self): @@ -463,7 +467,7 @@ def get_dict(self): } -class VersionDiagnostics(Diagnostics): +class VersionDiagnostics(DiagnosticsBase): """Provides version numbers from relevant libraries.""" def get_dict(self): @@ -485,7 +489,7 @@ def get_dict(self): return info -class ObjectCountDiagnostics(Diagnostics): +class ObjectCountDiagnostics(DiagnosticsBase): """Provides object counts and resource consumption, used in _classes.py.""" def __init__(self, name): diff --git a/wgpu/backends/wgpu_native/_helpers.py b/wgpu/backends/wgpu_native/_helpers.py index 691f6530..7b35844a 100644 --- a/wgpu/backends/wgpu_native/_helpers.py +++ b/wgpu/backends/wgpu_native/_helpers.py @@ -6,7 +6,7 @@ import ctypes from ._ffi import ffi, lib -from ..._diagnostics import Diagnostics +from ..._diagnostics import DiagnosticsBase from ...classes import ( GPUError, GPUOutOfMemoryError, @@ -357,7 +357,7 @@ def generate_report(): return report -class WgpuNativeCountsDiagnostics(Diagnostics): +class WgpuNativeCountsDiagnostics(DiagnosticsBase): def get_subscript(self): text = "" text += " * The a, k, r, e are allocated, kept, released, and error, respectively.\n"