Changelog#

The format is based on Keep a Changelog.

[1.1.12] - 2026-05-26#

Fixed#

  • OMPE-93585: test_non_terminal_double_star_with_predicate_does_not_bypass_subsequent_tokens now uses the canonical bracketed wildcard query shape Frame/**/Button[*].identifier=='target' instead of the unbracketed Frame/**/Button.identifier=='target'. Matches the form 50+ downstream omni.kit.ui_test.find() callers use and routes the token through _find_children_by_type as intended — the Type.predicate==value shape (no brackets) goes down a different dispatcher path that does not exercise the class-filtered descendant expansion the regression test is meant to cover. (Jeffrey Qiu review feedback 2026-05-26.)

[1.1.11] - 2026-05-26#

Fixed#

  • OMPE-93585: Guard widget tree traversal against None children. ui.Inspector.get_children can legitimately return nullptr entries for native widgets (notably ui.TreeView with a hidden header — see TreeView::_getChildren in omni.ui). Previously find_widgets / find_first_widget recursed into those entries, calling Inspector.get_children / hasattr / eval("w."+predicate) on None and on partially-destroyed widgets, which crashed the Kit process with SIGSEGV when omni.kit.ui_test.find() targeted the Script Editor window. All traversal call sites now funnel through a single defensive _safe_get_children chokepoint that filters out None and converts C++ binding faults into a benign empty list. Predicate evaluation is wrapped in a per-widget try/except so a single bad widget no longer kills the whole query. The get_widget_path, get_widget_children_with_path, and get_window_widget_paths layout helpers were also routed through _safe_get_children so the same defensive behaviour applies consistently across the codebase. (CodeRabbit feedback id 54571668)

  • _get_descendants was converted from recursive to iterative form to avoid Python recursion-limit blow-ups on deeply nested widget trees (e.g. Script Editor LogView). The new iterative form pushes children onto the stack with stack.extend(reversed(...)) so the leftmost sibling pops first and the historical deterministic left-to-right traversal order is preserved — 50+ downstream ui_test.find() callers across omni.kit.menu.utils, omni.kit.property.usd, omni.kit.widget.stage, omni.kit.window.inspector, and omni.kit.widget.searchfield rely on that order to pick “the first” match. The same reversed-extend treatment is applied in _get_descendants_with_predicate. (CodeRabbit feedback id 54373939)

  • _safe_predicate_filter and the inner _safe_eval_predicate (used by _get_descendants_with_predicate) no longer derive an attribute name with predicate.split('=')[0] and gate evaluation on hasattr. That gate silently dropped every predicate shape that was not a plain attr=='value' equality — identifier!='x' became "identifier!", width>10 had no = at all (so the attribute became the full expression), and method/property expressions such as identifier.startswith('foo') were always skipped. TUTORIAL.md guarantees that “the predicate can be any valid python expression”, so the gate violated the documented contract. The per-widget try/except already keeps a bad widget from crashing traversal, so the gate is redundant and has been removed. (review feedback id 54120186)

  • find_widget now routes its diagnostic “did find widgets” log through _safe_get_children, so None placeholders emitted by native widgets such as ui.TreeView never reach the comprehension that reads a.identifier. The attribute access inside the comprehension is additionally wrapped in getattr / try so a partially-destroyed native widget that raises during attribute lookup degrades to a placeholder name instead of taking down the diagnostic log site. (review feedback id 54120188)

  • Scoped the ** + predicate short-circuit in _process_widget_tokens to the case where ** is the FINAL widget token. For queries such as Frame/**/Button.identifier=='foo' the ** is non-terminal: the trailing Button token still has to filter the descendants discovered by ** before the final predicate is applied. The previous unconditional short-circuit returned the first descendant whose predicate matched even when the trailing token ruled it out (e.g. a Label with identifier=='foo' rather than a Button), because the remaining path tokens never ran. _get_descendants_with_predicate gained an is_final flag so the helper’s per-widget return only fires when the caller actually wants the predicate-as-final-filter semantics. (CodeRabbit feedback id 54616202)

Added#

  • _find_children_by_type now treats the class-name token * as a wildcard and honours a bracketed integer index. Query segments *[*], *[0], *[N] all match the appropriate children regardless of class — *[*] returns every child, *[N] returns the Nth child in declaration order. Previously the dispatcher only routed tokens containing the literal substring [*] to this helper, so *[N] fell through to _child_widget and silently returned None (no class is literally named *). This makes the documented contract from TUTORIAL.md actually hold, and gives the literal bug-trigger pattern from the OMPE-93585 Isaac Sim report (Frame/**/*[*]) — plus the indexed shape — a semantically meaningful match instead of an empty result. (CodeRabbit feedback id 54571676)

Security#

  • Added a prominent module-level SECURITY NOTE block and a warning admonition on the OmniUIQuery class docstring making it explicit that the .<predicate> tail of a query string is evaluated with the built-in :func:eval, that OmniUIQuery is a test helper, and that callers MUST treat predicate strings as trusted in-process test-code input. eval is deliberately kept (rather than swapped for a constrained AST evaluator) because TUTORIAL.md guarantees “the predicate can be any valid python expression” and 50+ downstream omni.kit.ui_test.find() callers rely on the full expression surface; restricting it would re-introduce the same class of regression the predicate-gate fix already had to undo. (CodeRabbit feedback id 54639054)

[1.1.10] - 2026-02-18#

Changed#

  • Changed find_menu_item description

[1.1.9] - 2025-11-07#

Fixed#

  • Make sure the queried window is a ui.Window type instead of ui.WindowHandle in case that it is destroyed

[1.1.8] - 2025-04-30#

Changed#

  • Updated documentation with AI agent.

[1.1.7] - 2024-11-07#

Changes#

  • OMPE-22724: Fix ui query get the same path for different widget issue.

[1.1.6] - 2024-10-28#

Changes#

  • Updated API and all exports

[1.1.5] - 2024-07-18#

Added#

  • OMPE-15065: Add function get_widget_children_with_path to get all child widgets with path for given widget and path

[1.1.4] - 2024-05-29#

Added#

  • Fix find_first_widget’s attribute error.

[1.1.3] - 2024-05-20#

Added#

  • Added function find_first_widget to get the first matched widget for a query.

[1.1.2] - 2024-01-29#

Added#

  • OM-119448: Added function get_window_widget_paths to get all widget paths of a window.

[1.1.1] - 2022-01-10#

  • support for widgets under Treeviews (i.e those created by the treeview delegate)

[1.1.0] - 2021-11-09#

  • (breaking!) Remove add to omni.ui namespace magic as it breaks intellisense

  • Add support for starting search from a set of root widgets

  • Fix few query search bugs

[1.0.1] - 2021-10-29#

Added#

  • When multiple windows with the same name are found, use the 1st visible one.

[1.0.0] - 2021-09-08#

Added#

  • The initial release