Architecture
Backend Architecture
This page is for contributors and extension authors. Most tests should use Desktop(backend="uia") or Desktop(backend="win32") and do not need the backend registry directly.
Runtime Path Used By Tests
The primary API path is:
test code
-> Desktop
-> Application
-> Window
-> Locator
-> pywinauto UIA or Win32 wrappers
Backend capability contract
Every Backend publishes a capabilities() frozenset naming
the Capability members it fully supports. Runtime operations
gated on a capability check raise UnsupportedCapabilityError
(with a hint pointing at working alternatives) when the current
backend does not declare support.
Introspection surfaces:
Desktop.backend_supports(cap)— for the currently-configured backendDesktop.require_capability(cap)— raises if missingSapGui.backend_supports(cap)/CDPSession.backend_supports(cap)/DelphiApp.backend_supports(cap)/MainframeTerminal.backend_supports(cap)/OracleFormsApp.backend_supports(cap)— per-facadesupported_backends(cap)— every registered backend id that supports the capabilitylist_backends()— full matrix, capabilities included
Third-party plugin backends declare capabilities() in their
Backend subclass and register through the dolphin_desktop.backends
entry-point group; capability queries flow through the registry
automatically.
Full guidance: backend-capabilities.md.
Two Action Paths — Input Simulation vs Programmatic
Every Locator (and every JABLocator) exposes actions along two
parallel families:
| Family | Dispatches | Requires an input desktop | Fires hover/keystroke handlers | Fails when unsupported |
|---|---|---|---|---|
| Input simulation | click(), type_text(), hover(), drag_to() |
Yes | Yes | Underlying SetCursorPos/SendInput error |
| Programmatic | invoke(), toggle(), expand(), collapse(), select(), set_value() |
No | No | UnsupportedPatternError — no silent fallback |
The two families exist so headless mode is usable for smoke tests
without lying to the caller about how the click was delivered. Selection of the right primitive
is a test-author decision, not a library fallback — the
programmatic family raises UnsupportedPatternError cleanly rather
than reaching for the mouse in the background.
Full guidance in tutorials/headless-mode.md.
Backend selection
Desktop passes its backend value to pywinauto. The documented values for application automation are:
| Value | Meaning |
|---|---|
uia |
Microsoft UI Automation. This is the default. |
win32 |
Win32 HWND backend for older controls and legacy applications. |
Image matching is not selected with Desktop(backend="image"). Use ImageLocator, Screen, window.image(...), or a locator image_fallback for that path.
Registry Module
dolphin_desktop._backend defines an abstract Backend interface and registry helpers:
| Object | Purpose |
|---|---|
Backend |
Abstract interface for backend experiments and plugins |
UIABackend |
Wrapper implementation for UIA operations |
Win32Backend |
Wrapper implementation for Win32 operations |
ImageBackend |
Template-matching backend object used by registry experiments |
register_backend |
Register a backend class in-process |
resolve_backend |
Instantiate a backend by ID |
list_backends |
Return metadata shown by dolphin info backends |
The registry is public API, but the current Desktop implementation does not route normal locator actions through resolve_backend.
Built-In Backend IDs
| ID | Class | Status |
|---|---|---|
uia |
UIABackend |
Implemented on Windows |
win32 |
Win32Backend |
Implemented on Windows |
qt |
QtBackend |
Implemented on Windows — UIA with a Qt-aware locator wrapper |
image |
ImageBackend |
Implemented when OpenCV is installed |
cdp |
CDPBackend |
Marker — real surface is CDPSession via Desktop.launch_electron_cdp() and friends |
sap |
SapBackend |
Marker — real surface is SapGui via Desktop.sap() |
delphi |
DelphiBackend |
Marker — real surface is DelphiApp via Desktop.launch_delphi() |
java |
JavaBackend |
Marker — real surface is JavaAccessBridge / OracleFormsApp via Desktop.launch_java() / Desktop.launch_oracle_forms() |
mainframe |
MainframeBackend |
Marker — real surface is MainframeTerminal via Desktop.mainframe() |
macos |
MacOSAccessibilityBackend |
Reserved stub, not implemented |
linux |
LinuxATSPIBackend |
Reserved stub, not implemented |
Marker backends appear in list_backends() and publish their stack's capability set, but direct calls on them raise UnsupportedCapabilityError with a hint pointing at the matching facade. The reserved stubs (macos, linux) return is_available() == False and also raise UnsupportedCapabilityError for backend methods.
Listing Backends
dolphin info backends
The command prints backend ID, target platform, availability on the current machine, source, and description.
Registering A Backend In Process
from dolphin_desktop import Backend, register_backend
@register_backend
class MyBackend(Backend):
id = "my_backend"
platform = "windows"
def find_element(self, parent, criteria):
...
def click(self, element, *, button="left"):
...
def type_text(self, element, text):
...
def get_tree(self, root, *, depth=None):
...
def screenshot(self, element=None):
...
Registering Through Package Metadata
Third-party packages can expose backends through the dolphin_desktop.backends entry-point group:
[project.entry-points."dolphin_desktop.backends"]
my_backend = "my_backend_pkg.my_backend:MyBackend"
Installed entry points are discovered by list_backends() and resolve_backend().