Skip to content

Pythonic API

Beyond the 1:1 FlaUI surface, the wrapper adds a few ergonomic, Python-native conveniences so tests read naturally and clean up after themselves. These are additive — the underlying FlaUI methods are unchanged.

Context managers

Application and the high-level Automation both support the with statement and dispose themselves on exit (graceful close, then dispose), so you never leak processes or COM objects:

from flaui.lib.pythonnet_bridge import setup_pythonnet_bridge
setup_pythonnet_bridge()

from flaui.modules.automation import Automation
from flaui.lib.enums import UIAutomationTypes

with Automation(UIAutomationTypes.UIA3) as automation:
    app = automation.application.launch("notepad.exe")
    main_window = app.get_main_window(automation)
    ...
# automation is disposed and the app is closed automatically here

__exit__ never raises: cleanup failures are swallowed so they cannot mask an error from inside the block.

Element collections

The find_all_* methods return an AutomationElementCollection. It is a list (indexing, slicing, len, iteration all work exactly as before), with a few helpers added:

buttons = main_window.find_all_descendants()

buttons.first              # first element, or None if empty
buttons.filter(lambda e: e.is_enabled)        # -> AutomationElementCollection
buttons.where(name="OK")                       # match by attribute equality
for element in buttons:                          # still a plain list under the hood
    print(element)

where(...) treats a missing/unreadable attribute as a non-match (it never raises), so it is safe across heterogeneous results.

Fluent assertions: expect(...)

expect(element) provides Playwright-style, auto-waiting assertions. Each matcher polls the element until the condition holds or the timeout elapses, then raises AssertionError with a clear message:

from flaui.core.expectations import expect

expect(ok_button).to_be_visible()
expect(ok_button).to_be_enabled(timeout=2000)   # per-call timeout (ms)
expect(checkbox).to_be_checked()
expect(label).to_have_text("Done")
expect(field).to_have_value("42")

expect(spinner).not_.to_be_visible(timeout=2000)  # negate any matcher with .not_

Available matchers: to_be_visible, to_be_enabled, to_be_offscreen, to_be_checked, to_have_name, to_have_text, to_have_value — each with an optional timeout (milliseconds) and a .not_ negation. Polling is built on the existing Retry helper, so timeouts and intervals behave consistently with the rest of the library (expect(el, timeout=5000, interval=100)).