Two related path-resolution issues in imgui_test_engine glue when items are nested inside child windows
Environment
imgui_bundle 1.92.601 (wheel, Python 3.13, Windows 11)
- Backend:
hello_imgui.run with app_window_params.hidden = True and use_imgui_test_engine = True (real GLFW backend, hidden window — same headless setup the test engine demos use; same behavior reproduced under default visible-window run)
gh --version for context: 2.89.0
I ran into two related issues while writing imgui_test_engine tests for a Python app whose layout uses imgui.begin_child(...) inside an imgui.columns(...) legacy block. The user-facing items (tree nodes, selectables) inside the child are not addressable from any path string I tried, and the wiki-recommended set_ref(window_info(...).window) overload aborts at the C++ layer.
Finding 1: items inside begin_child unreachable via every probed path string
ctx.item_exists returns False for items inside begin_child regardless of the path form, while items at the parent-window scope resolve normally.
Minimal repro (full script below):
imgui.begin("Window", True)
imgui.button("Close##sv_close") # parent-window item
imgui.begin_child("foo_child", imgui.ImVec2(0.0, 0.0))
for i in range(20):
imgui.tree_node(f"Animal {i}###sv_animal_BM{i}") # child-window items
imgui.end_child()
imgui.end()
Test script (after ctx.set_ref("Window") + 8 yields):
| Path probed |
ctx.item_exists |
Close##sv_close |
True |
//Window/Close##sv_close |
True |
###sv_animal_BM6 |
False |
Window/###sv_animal_BM6 |
False |
Window/foo_child/###sv_animal_BM6 |
False |
foo_child/###sv_animal_BM6 |
False |
//foo_child/###sv_animal_BM6 |
False |
//Window/foo_child/###sv_animal_BM6 |
False |
Also: ctx.window_info("foo_child") returns a non-null window object but reports id == 0. (The wiki note at the binding's test_engine.pyi line 1473 says child-window paths are "internally mangled" — but no path I formed found the items.)
Finding 2: ctx.set_ref(ctx.window_info(name).window) hard-aborts at C++
The wiki-recommended next step (binding pyi line 1473):
SetRef(WindowInfo("Parent/Child")->Window) --> set ref to child window.
Translated literally:
info = ctx.window_info("foo_child")
ctx.set_ref(info.window) # <-- hard-aborts at the C++ layer
The Python process exits with Fatal Python error: Aborted immediately on the set_ref(...) call (no Python exception, no engine log entry, no status from the test runner). No stack frames in the user code — the abort is from native assert() somewhere inside ImGuiTestContext::SetRef(ImGuiWindow*).
Workaround attempted: ctx.set_ref("foo_child") (bare child str_id) — runs without abort, but subsequent ctx.item_exists("###sv_animal_BM6") still returns False (Finding 1 again).
Why this matters
I'm building a section-viewer in a Python app with ~970 sections across 39 animals. The user needs native imgui.begin_child for the animal-list scroll (manual-scroll mechanism doesn't deliver real wheel input from real hardware reliably). With this binding limitation, our entire test suite (~28 call sites that navigate tree_node → selectable to pick a section) cannot exercise the live render path. We worked around it with a hybrid render-mode flag (live GUI = begin_child, test backend = parent-scope render via a module flag toggled by the test runner), but that means our test suite no longer exercises the actual production rendering of the browser.
A fix to either (a) make path strings resolve into child windows, or (b) make set_ref(window_info(...).window) not abort, would let us drop the hybrid path and test the production render directly.
Full minimal repro script
"""Minimal repro: items inside ``begin_child`` unreachable via path
strings; ``set_ref(window_info(...).window)`` aborts at C++.
Run: pixi run -e delta python this_file.py (or any imgui_bundle env).
"""
from __future__ import annotations
from imgui_bundle import hello_imgui, imgui
def gui() -> None:
imgui.set_next_window_size(imgui.ImVec2(420.0, 360.0), imgui.Cond_.first_use_ever.value)
imgui.begin("Window", True)
if imgui.button("Close##sv_close"):
pass
imgui.text("---")
visible = imgui.begin_child("foo_child", imgui.ImVec2(0.0, 0.0))
if visible:
for i in range(20):
imgui.tree_node(f"Animal {i}###sv_animal_BM{i}")
imgui.end_child()
imgui.end()
_state: dict = {"test": None}
def register_tests() -> None:
engine = hello_imgui.get_imgui_test_engine()
te = imgui.test_engine
io = te.get_io(engine)
io.config_run_speed = te.TestRunSpeed.fast
io.config_no_throttle = True
io.config_log_to_tty = False
io.config_capture_enabled = False
io.config_watchdog_kill_test = 15.0
io.config_watchdog_kill_app = 20.0
test = te.register_test(engine, "spike", "child_path_resolve")
_state["test"] = test
def script(ctx) -> None:
ctx.set_ref("Window")
for _ in range(8):
ctx.yield_()
# Parent-window controls (these both work).
print(
f"[parent] Close##sv_close exists at parent ref: "
f"{ctx.item_exists('Close##sv_close')!r}"
)
print(
f"[parent] //Window/Close##sv_close exists: "
f"{ctx.item_exists('//Window/Close##sv_close')!r}"
)
# Items inside begin_child (all 6 path variants tried).
for path in [
"###sv_animal_BM6",
"Window/###sv_animal_BM6",
"Window/foo_child/###sv_animal_BM6",
"foo_child/###sv_animal_BM6",
"//foo_child/###sv_animal_BM6",
"//Window/foo_child/###sv_animal_BM6",
]:
try:
ok = ctx.item_exists(path)
except Exception as exc:
ok = f"err={exc!r}"
print(f"[child] {path:<55} exists: {ok!r}")
info = ctx.window_info("foo_child")
is_none = getattr(info, "window", None) is None
wid = int(getattr(info, "id", 0))
print(
f"[child] window_info('foo_child').window is_none={is_none}, id={wid}"
)
# The line below is the wiki-recommended pattern; it
# HARD-ABORTS at the C++ layer on this binding. Commented
# so the script can complete.
# ctx.set_ref(info.window)
try:
ctx.engine.app_shall_exit = True
except Exception:
pass
test.test_func = script
te.queue_test(engine, test)
def post_new_frame() -> None:
test = _state.get("test")
if test is None:
return
te = imgui.test_engine
status = test.output.status
live = hello_imgui.get_runner_params()
if status == te.TestStatus.success or status == te.TestStatus.error:
live.app_shall_exit = True
elif live.app_shall_exit:
live.app_shall_exit = False
def main() -> None:
rp = hello_imgui.RunnerParams()
rp.app_window_params.window_title = "spike_child_path_resolve"
rp.app_window_params.window_geometry.size = (640, 480)
rp.app_window_params.hidden = True
rp.use_imgui_test_engine = True
rp.ini_disable = True
rp.callbacks.show_gui = gui
rp.callbacks.register_tests = register_tests
rp.callbacks.post_new_frame = post_new_frame
hello_imgui.run(rp)
if __name__ == "__main__":
main()
Repro output (literal)
[parent] Close##sv_close exists at parent ref: True
[parent] //Window/Close##sv_close exists: True
[child] ###sv_animal_BM6 exists: False
[child] Window/###sv_animal_BM6 exists: False
[child] Window/foo_child/###sv_animal_BM6 exists: False
[child] foo_child/###sv_animal_BM6 exists: False
[child] //foo_child/###sv_animal_BM6 exists: False
[child] //Window/foo_child/###sv_animal_BM6 exists: False
[child] window_info('foo_child').window is_none=False, id=0
Uncomment the ctx.set_ref(info.window) line near the bottom of the script to reproduce Finding 2 (Python exits with Fatal Python error: Aborted immediately on that call; no engine log, no Python stack frame in user code).
What I'm hoping for
Either (a) make ctx.item_exists("Window/foo_child/###sv_animal_BM6") (or some documented form) resolve, or (b) make ctx.set_ref(window_info("foo_child").window) not abort. Happy to test a candidate fix on the same setup if a maintainer wants me to.
Two related path-resolution issues in imgui_test_engine glue when items are nested inside child windows
Environment
imgui_bundle1.92.601 (wheel, Python 3.13, Windows 11)hello_imgui.runwithapp_window_params.hidden = Trueanduse_imgui_test_engine = True(real GLFW backend, hidden window — same headless setup the test engine demos use; same behavior reproduced under default visible-window run)gh --versionfor context:2.89.0I ran into two related issues while writing
imgui_test_enginetests for a Python app whose layout usesimgui.begin_child(...)inside animgui.columns(...)legacy block. The user-facing items (tree nodes, selectables) inside the child are not addressable from any path string I tried, and the wiki-recommendedset_ref(window_info(...).window)overload aborts at the C++ layer.Finding 1: items inside
begin_childunreachable via every probed path stringctx.item_existsreturnsFalsefor items insidebegin_childregardless of the path form, while items at the parent-window scope resolve normally.Minimal repro (full script below):
Test script (after
ctx.set_ref("Window")+ 8 yields):ctx.item_existsClose##sv_closeTrue//Window/Close##sv_closeTrue###sv_animal_BM6FalseWindow/###sv_animal_BM6FalseWindow/foo_child/###sv_animal_BM6Falsefoo_child/###sv_animal_BM6False//foo_child/###sv_animal_BM6False//Window/foo_child/###sv_animal_BM6FalseAlso:
ctx.window_info("foo_child")returns a non-nullwindowobject but reportsid == 0. (The wiki note at the binding'stest_engine.pyiline 1473 says child-window paths are "internally mangled" — but no path I formed found the items.)Finding 2:
ctx.set_ref(ctx.window_info(name).window)hard-aborts at C++The wiki-recommended next step (binding pyi line 1473):
Translated literally:
The Python process exits with
Fatal Python error: Abortedimmediately on theset_ref(...)call (no Python exception, no engine log entry, no status from the test runner). No stack frames in the user code — the abort is from nativeassert()somewhere insideImGuiTestContext::SetRef(ImGuiWindow*).Workaround attempted:
ctx.set_ref("foo_child")(bare child str_id) — runs without abort, but subsequentctx.item_exists("###sv_animal_BM6")still returnsFalse(Finding 1 again).Why this matters
I'm building a section-viewer in a Python app with ~970 sections across 39 animals. The user needs native
imgui.begin_childfor the animal-list scroll (manual-scroll mechanism doesn't deliver real wheel input from real hardware reliably). With this binding limitation, our entire test suite (~28 call sites that navigatetree_node→selectableto pick a section) cannot exercise the live render path. We worked around it with a hybrid render-mode flag (live GUI =begin_child, test backend = parent-scope render via a module flag toggled by the test runner), but that means our test suite no longer exercises the actual production rendering of the browser.A fix to either (a) make path strings resolve into child windows, or (b) make
set_ref(window_info(...).window)not abort, would let us drop the hybrid path and test the production render directly.Full minimal repro script
Repro output (literal)
Uncomment the
ctx.set_ref(info.window)line near the bottom of the script to reproduce Finding 2 (Python exits withFatal Python error: Abortedimmediately on that call; no engine log, no Python stack frame in user code).What I'm hoping for
Either (a) make
ctx.item_exists("Window/foo_child/###sv_animal_BM6")(or some documented form) resolve, or (b) makectx.set_ref(window_info("foo_child").window)not abort. Happy to test a candidate fix on the same setup if a maintainer wants me to.