imgui_debugger 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.
- imgui_debugger/__init__.py +88 -0
- imgui_debugger/_assets.py +125 -0
- imgui_debugger/debugger.py +553 -0
- imgui_debugger/edit.py +125 -0
- imgui_debugger/format.py +130 -0
- imgui_debugger/py.typed +0 -0
- imgui_debugger/runner.py +91 -0
- imgui_debugger/scopes.py +734 -0
- imgui_debugger/search.py +170 -0
- imgui_debugger/theme.py +129 -0
- imgui_debugger/tree.py +285 -0
- imgui_debugger-0.1.0.dist-info/METADATA +225 -0
- imgui_debugger-0.1.0.dist-info/RECORD +16 -0
- imgui_debugger-0.1.0.dist-info/WHEEL +5 -0
- imgui_debugger-0.1.0.dist-info/licenses/LICENSE +21 -0
- imgui_debugger-0.1.0.dist-info/top_level.txt +1 -0
imgui_debugger/format.py
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""One-line renderings of arbitrary Python values for the tree's right column.
|
|
2
|
+
|
|
3
|
+
Examples
|
|
4
|
+
--------
|
|
5
|
+
>>> from imgui_debugger.format import fmt_value, type_label
|
|
6
|
+
>>> fmt_value(3.5)
|
|
7
|
+
'3.5'
|
|
8
|
+
>>> type_label([1, 2, 3])
|
|
9
|
+
'list[3]'
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from collections.abc import Mapping, Sequence
|
|
15
|
+
|
|
16
|
+
MAX_STR = 256
|
|
17
|
+
MAX_INLINE = 8
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def is_container(value) -> bool:
|
|
21
|
+
"""True when a value is a mapping or a non-string sequence.
|
|
22
|
+
|
|
23
|
+
Examples
|
|
24
|
+
--------
|
|
25
|
+
>>> from imgui_debugger.format import is_container
|
|
26
|
+
>>> is_container({"a": 1}), is_container("abc"), is_container([1])
|
|
27
|
+
(True, False, True)
|
|
28
|
+
"""
|
|
29
|
+
if isinstance(value, Mapping):
|
|
30
|
+
return True
|
|
31
|
+
return isinstance(value, Sequence) and not isinstance(value, (str, bytes, bytearray))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def type_label(value) -> str:
|
|
35
|
+
"""The type name, with a length or shape suffix when the value has one.
|
|
36
|
+
|
|
37
|
+
Examples
|
|
38
|
+
--------
|
|
39
|
+
>>> from imgui_debugger.format import type_label
|
|
40
|
+
>>> type_label({"a": 1})
|
|
41
|
+
'dict[1]'
|
|
42
|
+
>>> type_label(2.0)
|
|
43
|
+
'float'
|
|
44
|
+
"""
|
|
45
|
+
name = type(value).__name__
|
|
46
|
+
shape = getattr(value, "shape", None)
|
|
47
|
+
if shape is not None and not callable(shape):
|
|
48
|
+
try:
|
|
49
|
+
return f"{name}{tuple(shape)}"
|
|
50
|
+
except TypeError:
|
|
51
|
+
return name
|
|
52
|
+
if is_container(value):
|
|
53
|
+
try:
|
|
54
|
+
return f"{name}[{len(value)}]"
|
|
55
|
+
except TypeError:
|
|
56
|
+
return name
|
|
57
|
+
return name
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def fmt_value(value, max_str: int = MAX_STR) -> str:
|
|
61
|
+
"""Format a value as one short line, never raising on a hostile ``__repr__``.
|
|
62
|
+
|
|
63
|
+
Parameters
|
|
64
|
+
----------
|
|
65
|
+
value : object
|
|
66
|
+
Anything.
|
|
67
|
+
max_str : int
|
|
68
|
+
Longest string rendered verbatim before truncation.
|
|
69
|
+
|
|
70
|
+
Examples
|
|
71
|
+
--------
|
|
72
|
+
>>> from imgui_debugger.format import fmt_value
|
|
73
|
+
>>> fmt_value(None), fmt_value(True), fmt_value(b"ab")
|
|
74
|
+
('None', 'True', '<2 bytes>')
|
|
75
|
+
>>> fmt_value([1, 2, 3])
|
|
76
|
+
'[1, 2, 3]'
|
|
77
|
+
"""
|
|
78
|
+
if value is None or isinstance(value, bool):
|
|
79
|
+
return repr(value)
|
|
80
|
+
if isinstance(value, str):
|
|
81
|
+
return repr(value if len(value) <= max_str else value[: max_str - 3] + "...")
|
|
82
|
+
if isinstance(value, (int, float, complex)):
|
|
83
|
+
return repr(value)
|
|
84
|
+
if isinstance(value, (bytes, bytearray)):
|
|
85
|
+
return f"<{len(value)} bytes>"
|
|
86
|
+
if hasattr(value, "shape") and hasattr(value, "dtype"):
|
|
87
|
+
return _fmt_array(value)
|
|
88
|
+
if isinstance(value, (tuple, list, set, frozenset)):
|
|
89
|
+
try:
|
|
90
|
+
if len(value) <= MAX_INLINE and all(
|
|
91
|
+
isinstance(v, (int, float, bool, str, type(None))) for v in value
|
|
92
|
+
):
|
|
93
|
+
return repr(value)
|
|
94
|
+
return f"<{type_label(value)}>"
|
|
95
|
+
except TypeError:
|
|
96
|
+
return f"<{type(value).__name__}>"
|
|
97
|
+
if isinstance(value, Mapping):
|
|
98
|
+
return f"<{type_label(value)}>"
|
|
99
|
+
if callable(value):
|
|
100
|
+
return f"<{type(value).__name__} {getattr(value, '__name__', '')}>".replace(" >", ">")
|
|
101
|
+
return f"<{type(value).__name__}>"
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _fmt_array(value) -> str:
|
|
105
|
+
"""Summarize a numpy-like array, inlining only tiny scalar-dtype ones.
|
|
106
|
+
|
|
107
|
+
Examples
|
|
108
|
+
--------
|
|
109
|
+
>>> import numpy as np # doctest: +SKIP
|
|
110
|
+
>>> _fmt_array(np.zeros((4, 4))) # doctest: +SKIP
|
|
111
|
+
'<shape=(4, 4), dtype=float64>'
|
|
112
|
+
"""
|
|
113
|
+
try:
|
|
114
|
+
if value.size <= MAX_INLINE and getattr(value.dtype, "kind", "") in "biufcSU":
|
|
115
|
+
return repr(value.tolist())
|
|
116
|
+
return f"<shape={tuple(value.shape)}, dtype={value.dtype}>"
|
|
117
|
+
except Exception:
|
|
118
|
+
return f"<{type(value).__name__}>"
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def fmt_error(exc: BaseException) -> str:
|
|
122
|
+
"""Format an exception raised while reading a value.
|
|
123
|
+
|
|
124
|
+
Examples
|
|
125
|
+
--------
|
|
126
|
+
>>> from imgui_debugger.format import fmt_error
|
|
127
|
+
>>> fmt_error(ValueError("bad shape"))
|
|
128
|
+
'<ValueError: bad shape>'
|
|
129
|
+
"""
|
|
130
|
+
return f"<{type(exc).__name__}: {exc}>"
|
imgui_debugger/py.typed
ADDED
|
File without changes
|
imgui_debugger/runner.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""One-shot harness: open the debugger in its own window and block until closed.
|
|
2
|
+
|
|
3
|
+
Examples
|
|
4
|
+
--------
|
|
5
|
+
>>> from imgui_debugger import run_debugger
|
|
6
|
+
>>> run_debugger({"fs": 9.6, "dz": 5.0}) # doctest: +SKIP
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import os
|
|
12
|
+
from typing import Optional
|
|
13
|
+
|
|
14
|
+
from ._assets import default_ini_path, ensure_assets
|
|
15
|
+
from .debugger import Debugger, DebuggerConfig
|
|
16
|
+
from .theme import to_vec4
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def run_debugger(
|
|
20
|
+
target=None,
|
|
21
|
+
config: Optional[DebuggerConfig] = None,
|
|
22
|
+
runner_params=None,
|
|
23
|
+
**kwargs,
|
|
24
|
+
) -> Debugger:
|
|
25
|
+
"""Show the debugger in its own OS window, blocking until it is closed.
|
|
26
|
+
|
|
27
|
+
Parameters
|
|
28
|
+
----------
|
|
29
|
+
target : object | None
|
|
30
|
+
The object to inspect.
|
|
31
|
+
config : DebuggerConfig | None
|
|
32
|
+
A prebuilt config; ``target`` and ``kwargs`` override its fields.
|
|
33
|
+
runner_params : hello_imgui.RunnerParams | None
|
|
34
|
+
Supply your own params, e.g. a null backend for headless tests. Window
|
|
35
|
+
title, size and ``.ini`` are filled in from the config only when unset.
|
|
36
|
+
**kwargs
|
|
37
|
+
Any :class:`~imgui_debugger.DebuggerConfig` field.
|
|
38
|
+
|
|
39
|
+
Returns
|
|
40
|
+
-------
|
|
41
|
+
Debugger
|
|
42
|
+
The widget that was rendered, so its final state can be read back.
|
|
43
|
+
|
|
44
|
+
Examples
|
|
45
|
+
--------
|
|
46
|
+
>>> from imgui_debugger import run_debugger
|
|
47
|
+
>>> class Settings:
|
|
48
|
+
... def __init__(self):
|
|
49
|
+
... self.threshold = 0.4
|
|
50
|
+
>>> run_debugger(Settings(), title="Settings") # doctest: +SKIP
|
|
51
|
+
>>> run_debugger({"a": 1}, private=True, editable=False) # doctest: +SKIP
|
|
52
|
+
"""
|
|
53
|
+
from imgui_bundle import hello_imgui, immapp
|
|
54
|
+
|
|
55
|
+
cfg = config or DebuggerConfig()
|
|
56
|
+
if target is not None:
|
|
57
|
+
cfg.target = target
|
|
58
|
+
for key, value in kwargs.items():
|
|
59
|
+
if not hasattr(cfg, key):
|
|
60
|
+
raise TypeError(f"DebuggerConfig has no field {key!r}")
|
|
61
|
+
setattr(cfg, key, value)
|
|
62
|
+
|
|
63
|
+
ensure_assets(cfg.assets_folder)
|
|
64
|
+
dbg = Debugger(cfg, frame_depth=2)
|
|
65
|
+
|
|
66
|
+
params = runner_params if runner_params is not None else hello_imgui.RunnerParams()
|
|
67
|
+
params.app_window_params.window_title = cfg.window_title or cfg.title or "Debugger"
|
|
68
|
+
if cfg.window_size:
|
|
69
|
+
params.app_window_params.window_geometry.size = tuple(cfg.window_size)
|
|
70
|
+
params.app_window_params.window_geometry.size_auto = False
|
|
71
|
+
params.app_window_params.resizable = cfg.resizable
|
|
72
|
+
# hello_imgui resolves ini_filename against the cwd by default, which drops
|
|
73
|
+
# a layout file wherever the app was launched; pin it to an absolute path.
|
|
74
|
+
if not params.ini_filename:
|
|
75
|
+
ini = cfg.ini_path or default_ini_path()
|
|
76
|
+
params.ini_filename = ini
|
|
77
|
+
if os.path.isabs(ini):
|
|
78
|
+
params.ini_folder_type = hello_imgui.IniFolderType.absolute_path
|
|
79
|
+
parent = os.path.dirname(ini)
|
|
80
|
+
if parent:
|
|
81
|
+
os.makedirs(parent, exist_ok=True)
|
|
82
|
+
bg = to_vec4(cfg.theme.bg)
|
|
83
|
+
params.imgui_window_params.background_color = bg
|
|
84
|
+
params.callbacks.show_gui = dbg.render
|
|
85
|
+
|
|
86
|
+
addons = immapp.AddOnsParams()
|
|
87
|
+
addons.with_markdown = False
|
|
88
|
+
addons.with_implot = False
|
|
89
|
+
addons.with_implot3d = False
|
|
90
|
+
immapp.run(runner_params=params, add_ons_params=addons)
|
|
91
|
+
return dbg
|