From fcb5910c26b5079b3c8fd5b64c715fd615953859 Mon Sep 17 00:00:00 2001 From: JeffreyChen Date: Thu, 25 Jun 2026 11:30:10 +0800 Subject: [PATCH] Clear SonarCloud and Codacy static-analysis backlog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolve the open SonarCloud (193) and Codacy (3) findings: - S1172 (141): accessibility backend ABC default methods now pass their args through to _unsupported(*context), so the explicit signatures required for fake-backend overrides (W0221) are kept without unused parameters. - S1192 (13): extract duplicated literals to module constants (command_schema "Native UI" x67, placeholders, ".approvals", flow_editor submodule paths). - Code smells: merge nested if (http_cassette), flatten nested ternaries (assertions, color_match), drop redundant FileNotFoundError (notifier), prune expired leases after iteration instead of list() copy (credential_broker), extract _signal_parts to cut cognitive complexity (element_scoring), unused locals -> _ (form_fields), valid NOSONAR syntax (stats), keep timeout param for 3.10 (webrtc_transport). - Security S8707 (4): justify the CLI/operator-supplied paths (config_bundle, turn_config, host_service, stubs) — not a remote trust boundary. - Tests: rename Image->pil_image, dict()->literal, comprehension->list(), drop gratuitous truthiness, reword code-like comments, guard the empty block, def instead of identity-check lambda, boto3 API names justified. - Frontend: self->globalThis (sw.js), aria-labels on inputs, valid NOSONAR on the non-module top-level-await line. - Codacy: guard QApplication create (W0106), nosemgrep on the protocol SHA-1 handshake and the local-CLI subprocess test. --- je_auto_control/gui/flow_editor/__init__.py | 10 +- .../gui/script_builder/command_schema.py | 218 +++++++++--------- .../utils/accessibility/backends/base.py | 68 +++--- je_auto_control/utils/assertion/assertions.py | 3 +- .../utils/color_match/color_match.py | 6 +- .../utils/config_bundle/__main__.py | 3 +- .../utils/element_scoring/element_scoring.py | 33 ++- .../utils/executor/action_executor.py | 7 +- .../utils/form_fields/form_fields.py | 4 +- .../utils/governance/credential_broker.py | 7 +- .../utils/http_cassette/http_cassette.py | 12 +- .../utils/mcp_server/tools/_handlers.py | 8 +- je_auto_control/utils/notify/notifier.py | 2 +- .../utils/remote_desktop/host_service.py | 3 +- .../utils/remote_desktop/turn_config.py | 3 +- .../remote_desktop/web_viewer/index.html | 6 +- .../utils/remote_desktop/web_viewer/sw.js | 6 +- .../utils/remote_desktop/webrtc_transport.py | 8 +- .../utils/rest_api/dashboard/swagger.html | 2 +- je_auto_control/utils/stats/stats.py | 8 +- je_auto_control/utils/stubs/generator.py | 3 +- .../headless/test_action_effect_batch.py | 2 +- .../headless/test_canonical_log_batch.py | 1 + .../headless/test_coordinate_space_batch.py | 6 +- .../headless/test_critic_features_batch.py | 2 +- .../headless/test_decision_table_batch.py | 4 +- test/unit_test/headless/test_diagnostics.py | 2 +- test/unit_test/headless/test_dotenv_batch.py | 2 +- .../headless/test_image_dedup_batch.py | 14 +- .../headless/test_link_header_batch.py | 2 +- .../headless/test_remote_desktop_cursor.py | 3 +- .../headless/test_remote_desktop_websocket.py | 2 +- .../unit_test/headless/test_s3_store_batch.py | 4 +- .../headless/test_schema_compat_batch.py | 2 +- .../headless/test_timeseries_batch.py | 2 +- .../unit_test/headless/test_window_capture.py | 8 +- 36 files changed, 257 insertions(+), 219 deletions(-) diff --git a/je_auto_control/gui/flow_editor/__init__.py b/je_auto_control/gui/flow_editor/__init__.py index 46560537..3a8fce49 100644 --- a/je_auto_control/gui/flow_editor/__init__.py +++ b/je_auto_control/gui/flow_editor/__init__.py @@ -17,11 +17,13 @@ FlowEdge, FlowLayout, FlowNodePosition, layout_steps, ) +_SCENE_MODULE = "je_auto_control.gui.flow_editor.scene" +_TAB_MODULE = "je_auto_control.gui.flow_editor.tab" _LAZY_SUBMODULES = { - "FlowEdgeItem": "je_auto_control.gui.flow_editor.scene", - "FlowGraphScene": "je_auto_control.gui.flow_editor.scene", - "FlowNodeItem": "je_auto_control.gui.flow_editor.scene", - "FlowEditorTab": "je_auto_control.gui.flow_editor.tab", + "FlowEdgeItem": _SCENE_MODULE, + "FlowGraphScene": _SCENE_MODULE, + "FlowNodeItem": _SCENE_MODULE, + "FlowEditorTab": _TAB_MODULE, } diff --git a/je_auto_control/gui/script_builder/command_schema.py b/je_auto_control/gui/script_builder/command_schema.py index ba1809b2..3c8c93fd 100644 --- a/je_auto_control/gui/script_builder/command_schema.py +++ b/je_auto_control/gui/script_builder/command_schema.py @@ -45,6 +45,16 @@ class CommandSpec: _MOUSE_BUTTONS = ("mouse_left", "mouse_right", "mouse_middle") _REGION_PLACEHOLDER = "[left, top, right, bottom]" +_NATIVE_UI = "Native UI" +_SCALES_PLACEHOLDER = "[0.9, 1.0, 1.1]" +_POINT_PLACEHOLDER = "[10, 20]" +_RECT_PLACEHOLDER = "[x, y, width, height]" +_RECT4_PLACEHOLDER = "[x, y, w, h]" +_APPROVALS_DIR = ".approvals" +_DOTTED_KEY_PLACEHOLDER = "db.host" +_POINTS_JSON_PLACEHOLDER = '[{"x":..,"y":..,"width":..,"height":..}]' +_MARKS_JSON_PLACEHOLDER = '[{"role":"button","name":"OK","x":..,"y":..}]' +_BOXES_JSON_PLACEHOLDER = '[{"role":"button","x":0,"y":0}]' def _build_specs() -> List[CommandSpec]: @@ -261,7 +271,7 @@ def _add_image_specs(specs: List[CommandSpec]) -> None: FieldSpec("min_score", FieldType.FLOAT, optional=True, default=0.8, min_value=0.0, max_value=1.0), FieldSpec("scales", FieldType.STRING, optional=True, - placeholder="[0.9, 1.0, 1.1]"), + placeholder=_SCALES_PLACEHOLDER), FieldSpec("region", FieldType.STRING, optional=True, placeholder=_REGION_PLACEHOLDER), ), @@ -313,7 +323,7 @@ def _add_image_specs(specs: List[CommandSpec]) -> None: FieldSpec("angles", FieldType.STRING, optional=True, placeholder="[-10, 0, 10]"), FieldSpec("scales", FieldType.STRING, optional=True, - placeholder="[0.9, 1.0, 1.1]"), + placeholder=_SCALES_PLACEHOLDER), FieldSpec("region", FieldType.STRING, optional=True, placeholder=_REGION_PLACEHOLDER), ), @@ -328,7 +338,7 @@ def _add_image_specs(specs: List[CommandSpec]) -> None: FieldSpec("angles", FieldType.STRING, optional=True, placeholder="[-10, 0, 10]"), FieldSpec("scales", FieldType.STRING, optional=True, - placeholder="[0.9, 1.0, 1.1]"), + placeholder=_SCALES_PLACEHOLDER), FieldSpec("max_results", FieldType.INT, optional=True, default=20), FieldSpec("nms_iou", FieldType.FLOAT, optional=True, default=0.3, min_value=0.0, max_value=1.0), @@ -344,7 +354,7 @@ def _add_image_specs(specs: List[CommandSpec]) -> None: FieldSpec("ambiguous_ratio", FieldType.FLOAT, optional=True, default=0.9, min_value=0.0, max_value=1.0), FieldSpec("scales", FieldType.STRING, optional=True, - placeholder="[0.9, 1.0, 1.1]"), + placeholder=_SCALES_PLACEHOLDER), FieldSpec("region", FieldType.STRING, optional=True, placeholder=_REGION_PLACEHOLDER), ), @@ -378,7 +388,7 @@ def _add_image_specs(specs: List[CommandSpec]) -> None: FieldSpec("min_score", FieldType.FLOAT, optional=True, default=0.7, min_value=0.0, max_value=1.0), FieldSpec("scales", FieldType.STRING, optional=True, - placeholder="[0.9, 1.0, 1.1]"), + placeholder=_SCALES_PLACEHOLDER), FieldSpec("region", FieldType.STRING, optional=True, placeholder=_REGION_PLACEHOLDER), ), @@ -684,7 +694,7 @@ def _add_image_specs(specs: List[CommandSpec]) -> None: "AC_fuse_elements", "Image", "Fuse Element Boxes", fields=( FieldSpec("ocr", FieldType.STRING, optional=True, - placeholder='[{"x":..,"y":..,"width":..,"height":..}]'), + placeholder=_POINTS_JSON_PLACEHOLDER), FieldSpec("icon", FieldType.STRING, optional=True), FieldSpec("a11y", FieldType.STRING, optional=True), FieldSpec("iou_threshold", FieldType.FLOAT, optional=True, default=0.9, @@ -696,7 +706,7 @@ def _add_image_specs(specs: List[CommandSpec]) -> None: "AC_reading_order", "Image", "Reading Order", fields=( FieldSpec("elements", FieldType.STRING, - placeholder='[{"x":..,"y":..,"width":..,"height":..}]'), + placeholder=_POINTS_JSON_PLACEHOLDER), FieldSpec("row_tol", FieldType.INT, optional=True, default=12), ), description="Order element boxes top-to-bottom, left-to-right (+ index).", @@ -705,7 +715,7 @@ def _add_image_specs(specs: List[CommandSpec]) -> None: "AC_locate_chain", "Image", "Locate Chain (refine boxes)", fields=( FieldSpec("boxes", FieldType.STRING, - placeholder='[{"x":..,"y":..,"width":..,"height":..}]'), + placeholder=_POINTS_JSON_PLACEHOLDER), FieldSpec("ops", FieldType.STRING, placeholder='[{"op":"filter","has_text":"OK"},{"op":"first"}]'), ), @@ -1062,7 +1072,7 @@ def _add_window_specs(specs: List[CommandSpec]) -> None: FieldSpec("paths", FieldType.STRING, placeholder='["C:\\\\a\\\\one.txt"]'), FieldSpec("point", FieldType.STRING, optional=True, - placeholder="[10, 20]"), + placeholder=_POINT_PLACEHOLDER), ), description="Drop files onto a window via WM_DROPFILES (Windows).", )) @@ -1072,7 +1082,7 @@ def _add_window_specs(specs: List[CommandSpec]) -> None: FieldSpec("paths", FieldType.STRING, placeholder='["C:\\\\a\\\\one.txt"]'), FieldSpec("point", FieldType.STRING, optional=True, - placeholder="[10, 20]"), + placeholder=_POINT_PLACEHOLDER), ), description="Build the WM_DROPFILES payload without sending (pure).", )) @@ -1220,7 +1230,7 @@ def _add_window_specs(specs: List[CommandSpec]) -> None: "bottom_right", "center", "left_third", "center_third", "right_third"), default="left"), FieldSpec("screen", FieldType.STRING, optional=True, - placeholder="[x, y, width, height]"), + placeholder=_RECT_PLACEHOLDER), FieldSpec("gap", FieldType.INT, optional=True, default=0), ), description="Compute the rectangle for a tiling slot of the screen.", @@ -1231,7 +1241,7 @@ def _add_window_specs(specs: List[CommandSpec]) -> None: FieldSpec("rows", FieldType.INT, default=2), FieldSpec("cols", FieldType.INT, default=2), FieldSpec("screen", FieldType.STRING, optional=True, - placeholder="[x, y, width, height]"), + placeholder=_RECT_PLACEHOLDER), FieldSpec("gap", FieldType.INT, optional=True, default=0), ), description="Compute the cell rectangles of an R×C screen grid.", @@ -1241,7 +1251,7 @@ def _add_window_specs(specs: List[CommandSpec]) -> None: fields=( FieldSpec("count", FieldType.INT, default=3), FieldSpec("screen", FieldType.STRING, optional=True, - placeholder="[x, y, width, height]"), + placeholder=_RECT_PLACEHOLDER), FieldSpec("offset", FieldType.INT, optional=True, default=30), FieldSpec("size", FieldType.STRING, optional=True, placeholder="[width, height]"), @@ -1549,67 +1559,67 @@ def _add_native_control_specs(specs: List[CommandSpec]) -> None: FieldSpec("automation_id", FieldType.STRING, optional=True), ) specs.append(CommandSpec( - "AC_control_get_value", "Native UI", "Get Control Value", + "AC_control_get_value", _NATIVE_UI, "Get Control Value", fields=fields, description="Read a native control's value via the accessibility API.", )) specs.append(CommandSpec( - "AC_control_set_value", "Native UI", "Set Control Value", + "AC_control_set_value", _NATIVE_UI, "Set Control Value", fields=(FieldSpec("value", FieldType.STRING),) + fields, description="Set a native control's value directly (no per-key typing).", )) specs.append(CommandSpec( - "AC_control_invoke", "Native UI", "Invoke Control", + "AC_control_invoke", _NATIVE_UI, "Invoke Control", fields=fields, description="Invoke a native control (e.g. press a button).", )) specs.append(CommandSpec( - "AC_control_toggle", "Native UI", "Toggle Control", + "AC_control_toggle", _NATIVE_UI, "Toggle Control", fields=fields, description="Toggle a native control (e.g. a checkbox).", )) specs.append(CommandSpec( - "AC_read_table", "Native UI", "Read Table / Grid", + "AC_read_table", _NATIVE_UI, "Read Table / Grid", fields=fields, description="Read a grid/table/list control as rows of cell strings.", )) specs.append(CommandSpec( - "AC_expand_control", "Native UI", "Expand Control", + "AC_expand_control", _NATIVE_UI, "Expand Control", fields=fields, description="Expand a tree node / combobox (ExpandCollapsePattern).", )) specs.append(CommandSpec( - "AC_collapse_control", "Native UI", "Collapse Control", + "AC_collapse_control", _NATIVE_UI, "Collapse Control", fields=fields, description="Collapse a tree node / combobox (ExpandCollapsePattern).", )) specs.append(CommandSpec( - "AC_control_expand_state", "Native UI", "Control Expand State", + "AC_control_expand_state", _NATIVE_UI, "Control Expand State", fields=fields, description="Read expanded/collapsed/partial/leaf state of a control.", )) specs.append(CommandSpec( - "AC_select_control_item", "Native UI", "Select Control Item", + "AC_select_control_item", _NATIVE_UI, "Select Control Item", fields=fields, description="Select a list / tree / tab item (SelectionItemPattern).", )) specs.append(CommandSpec( - "AC_control_range", "Native UI", "Get Control Range", + "AC_control_range", _NATIVE_UI, "Get Control Range", fields=fields, description="Read a slider / progress range (RangeValuePattern).", )) specs.append(CommandSpec( - "AC_set_control_range", "Native UI", "Set Control Range", + "AC_set_control_range", _NATIVE_UI, "Set Control Range", fields=(FieldSpec("value", FieldType.FLOAT),) + fields, description="Set a slider / progress / spinner value (RangeValuePattern).", )) specs.append(CommandSpec( - "AC_scroll_control_into_view", "Native UI", "Scroll Control Into View", + "AC_scroll_control_into_view", _NATIVE_UI, "Scroll Control Into View", fields=fields, description="Scroll a control into view (ScrollItemPattern).", )) specs.append(CommandSpec( - "AC_realize_item", "Native UI", "Realize Virtualized Item", + "AC_realize_item", _NATIVE_UI, "Realize Virtualized Item", fields=( FieldSpec("item_name", FieldType.STRING), FieldSpec("by", FieldType.ENUM, optional=True, default="name", @@ -1622,124 +1632,124 @@ def _add_native_control_specs(specs: List[CommandSpec]) -> None: description="Realize an off-screen item in a virtualized list/grid.", )) specs.append(CommandSpec( - "AC_get_element_properties", "Native UI", "Get Element Properties", + "AC_get_element_properties", _NATIVE_UI, "Get Element Properties", fields=fields, description="Read rich UIA props (enabled/offscreen/help/status/keys).", )) specs.append(CommandSpec( - "AC_table_headers", "Native UI", "Get Table Headers", + "AC_table_headers", _NATIVE_UI, "Get Table Headers", fields=fields, description="Read a table's row/column header labels (TablePattern).", )) specs.append(CommandSpec( - "AC_table_cell", "Native UI", "Get Table Cell (by index)", + "AC_table_cell", _NATIVE_UI, "Get Table Cell (by index)", fields=(FieldSpec("row", FieldType.INT), FieldSpec("column", FieldType.INT)) + fields, description="Read the cell at (row, column) with its span.", )) specs.append(CommandSpec( - "AC_cell_by_header", "Native UI", "Get Table Cell (by header)", + "AC_cell_by_header", _NATIVE_UI, "Get Table Cell (by header)", fields=(FieldSpec("row", FieldType.INT), FieldSpec("column_header", FieldType.STRING)) + fields, description="Read the cell at (row, named column) — assert by header.", )) specs.append(CommandSpec( - "AC_move_element", "Native UI", "Move Element (Transform)", + "AC_move_element", _NATIVE_UI, "Move Element (Transform)", fields=(FieldSpec("x", FieldType.FLOAT), FieldSpec("y", FieldType.FLOAT)) + fields, description="Move a UIA element to (x, y) (TransformPattern).", )) specs.append(CommandSpec( - "AC_resize_element", "Native UI", "Resize Element (Transform)", + "AC_resize_element", _NATIVE_UI, "Resize Element (Transform)", fields=(FieldSpec("width", FieldType.FLOAT), FieldSpec("height", FieldType.FLOAT)) + fields, description="Resize a UIA element (TransformPattern).", )) specs.append(CommandSpec( - "AC_set_window_state", "Native UI", "Set Window State", + "AC_set_window_state", _NATIVE_UI, "Set Window State", fields=(FieldSpec("state", FieldType.ENUM, default="normal", choices=("normal", "maximized", "minimized")),) + fields, description="Minimize / maximize / restore a window (WindowPattern).", )) specs.append(CommandSpec( - "AC_window_interaction_state", "Native UI", "Window Interaction State", + "AC_window_interaction_state", _NATIVE_UI, "Window Interaction State", fields=fields, description="Read window readiness (ready/blocked_by_modal/...).", )) specs.append(CommandSpec( - "AC_legacy_info", "Native UI", "Legacy (MSAA) Info", + "AC_legacy_info", _NATIVE_UI, "Legacy (MSAA) Info", fields=fields, description="Read an old control's MSAA info (LegacyIAccessible).", )) specs.append(CommandSpec( - "AC_legacy_default_action", "Native UI", "Legacy (MSAA) Default Action", + "AC_legacy_default_action", _NATIVE_UI, "Legacy (MSAA) Default Action", fields=fields, description="Fire an old control's MSAA default action (fallback).", )) specs.append(CommandSpec( - "AC_get_selection", "Native UI", "Get Container Selection", + "AC_get_selection", _NATIVE_UI, "Get Container Selection", fields=fields, description="Read a container's selection (SelectionPattern).", )) specs.append(CommandSpec( - "AC_list_views", "Native UI", "List Control Views", + "AC_list_views", _NATIVE_UI, "List Control Views", fields=fields, description="List a control's selectable views (MultipleViewPattern).", )) specs.append(CommandSpec( - "AC_set_view", "Native UI", "Set Control View", + "AC_set_view", _NATIVE_UI, "Set Control View", fields=(FieldSpec("view", FieldType.STRING),) + fields, description="Switch a control to the named view (MultipleViewPattern).", )) specs.append(CommandSpec( - "AC_wait_for_focus_change", "Native UI", "Wait for Focus Change", + "AC_wait_for_focus_change", _NATIVE_UI, "Wait for Focus Change", fields=(FieldSpec("timeout", FieldType.FLOAT, optional=True, default=5.0),), description="Block until keyboard focus moves (real UIA focus event).", )) specs.append(CommandSpec( - "AC_get_control_text", "Native UI", "Get Control Text", + "AC_get_control_text", _NATIVE_UI, "Get Control Text", fields=fields, description="Read full text via TextPattern (multiline / document safe).", )) specs.append(CommandSpec( - "AC_find_control_text", "Native UI", "Find Text in Control", + "AC_find_control_text", _NATIVE_UI, "Find Text in Control", fields=(FieldSpec("text", FieldType.STRING), FieldSpec("ignore_case", FieldType.BOOL, optional=True, default=True)) + fields, description="Whether text occurs in a control (TextPattern.FindText).", )) specs.append(CommandSpec( - "AC_select_control_text", "Native UI", "Select Text in Control", + "AC_select_control_text", _NATIVE_UI, "Select Text in Control", fields=(FieldSpec("text", FieldType.STRING), FieldSpec("ignore_case", FieldType.BOOL, optional=True, default=True)) + fields, description="Find + select text in a control (FindText + Select).", )) specs.append(CommandSpec( - "AC_control_text_attributes", "Native UI", "Get Text Attributes", + "AC_control_text_attributes", _NATIVE_UI, "Get Text Attributes", fields=fields, description="Read selection formatting (font/size/bold/italic/colour).", )) specs.append(CommandSpec( - "AC_get_selected_text", "Native UI", "Get Selected Text", + "AC_get_selected_text", _NATIVE_UI, "Get Selected Text", fields=fields, description="Read the currently selected text via TextPattern.", )) specs.append(CommandSpec( - "AC_get_visible_text", "Native UI", "Get Visible Text", + "AC_get_visible_text", _NATIVE_UI, "Get Visible Text", fields=fields, description="Read only the on-screen text via TextPattern.GetVisibleRanges.", )) specs.append(CommandSpec( - "AC_walk_tree", "Native UI", "Walk Accessibility Tree", + "AC_walk_tree", _NATIVE_UI, "Walk Accessibility Tree", fields=(FieldSpec("app_name", FieldType.STRING, optional=True), FieldSpec("max_results", FieldType.INT, optional=True, default=500)), description="Dump the a11y tree with friendly roles + a path per node.", )) specs.append(CommandSpec( - "AC_humanize_role", "Native UI", "Humanize UIA Role", + "AC_humanize_role", _NATIVE_UI, "Humanize UIA Role", fields=(FieldSpec("role", FieldType.STRING),), description="Translate a raw UIA role (ControlType_50000) to a name.", )) @@ -1747,17 +1757,17 @@ def _add_native_control_specs(specs: List[CommandSpec]) -> None: FieldSpec("max_results", FieldType.INT, optional=True, default=500)) specs.append(CommandSpec( - "AC_tab_order", "Native UI", "Keyboard Tab Order", + "AC_tab_order", _NATIVE_UI, "Keyboard Tab Order", fields=tree_fields, description="List focusable controls in keyboard Tab (reading) order.", )) specs.append(CommandSpec( - "AC_audit_focus_order", "Native UI", "Audit Focus Order (WCAG)", + "AC_audit_focus_order", _NATIVE_UI, "Audit Focus Order (WCAG)", fields=tree_fields, description="WCAG 2.4.x focus-order audit: tab sequence + flagged issues.", )) specs.append(CommandSpec( - "AC_focus_control", "Native UI", "Set Keyboard Focus", + "AC_focus_control", _NATIVE_UI, "Set Keyboard Focus", fields=fields, description="Set keyboard focus on a control natively (UIA SetFocus).", )) @@ -1862,7 +1872,7 @@ def _add_misc_specs(specs: List[CommandSpec]) -> None: description="Generate a TOTP 2FA code from a base32 secret.", )) specs.append(CommandSpec( - "AC_handle_file_dialog", "Native UI", "Handle File Dialog", + "AC_handle_file_dialog", _NATIVE_UI, "Handle File Dialog", fields=( FieldSpec("path", FieldType.STRING), FieldSpec("action", FieldType.ENUM, @@ -2055,7 +2065,7 @@ def _add_misc_specs(specs: List[CommandSpec]) -> None: FieldSpec("name", FieldType.STRING, placeholder="login_screen"), FieldSpec("content", FieldType.STRING), FieldSpec("approvals_dir", FieldType.STRING, optional=True, - default=".approvals"), + default=_APPROVALS_DIR), FieldSpec("extension", FieldType.STRING, optional=True, default="txt"), ), @@ -2066,7 +2076,7 @@ def _add_misc_specs(specs: List[CommandSpec]) -> None: fields=( FieldSpec("name", FieldType.STRING), FieldSpec("approvals_dir", FieldType.STRING, optional=True, - default=".approvals"), + default=_APPROVALS_DIR), FieldSpec("extension", FieldType.STRING, optional=True, default="txt"), ), @@ -2075,7 +2085,7 @@ def _add_misc_specs(specs: List[CommandSpec]) -> None: specs.append(CommandSpec( "AC_pending_artifacts", "Testing", "Approval: List Pending", fields=(FieldSpec("approvals_dir", FieldType.STRING, optional=True, - default=".approvals"),), + default=_APPROVALS_DIR),), description="List artifacts awaiting approval.", )) specs.append(CommandSpec( @@ -2730,7 +2740,7 @@ def _add_misc_specs(specs: List[CommandSpec]) -> None: FieldSpec("key", FieldType.STRING), FieldSpec("method", FieldType.STRING, placeholder="vlm/image"), FieldSpec("coordinates", FieldType.STRING, optional=True, - placeholder="[10, 20]"), + placeholder=_POINT_PLACEHOLDER), FieldSpec("description", FieldType.STRING, optional=True), FieldSpec("confidence", FieldType.FLOAT, optional=True, default=1.0), @@ -3247,7 +3257,7 @@ def _add_resilience_specs(specs: List[CommandSpec]) -> None: fields=( FieldSpec("layers", FieldType.STRING, placeholder='[{"name": "defaults", "mapping": {}}]'), - FieldSpec("key", FieldType.STRING, placeholder="db.host"), + FieldSpec("key", FieldType.STRING, placeholder=_DOTTED_KEY_PLACEHOLDER), ), description="Show the value and winning layer for a dotted config key.", )) @@ -3375,7 +3385,7 @@ def _add_resilience_specs(specs: List[CommandSpec]) -> None: "AC_cas_put", "Flow", "Optimistic: Put (CAS)", fields=( FieldSpec("name", FieldType.STRING, placeholder="config"), - FieldSpec("key", FieldType.STRING, placeholder="db.host"), + FieldSpec("key", FieldType.STRING, placeholder=_DOTTED_KEY_PLACEHOLDER), FieldSpec("value", FieldType.STRING, placeholder='"prod-1"'), FieldSpec("expected_version", FieldType.INT, optional=True), ), @@ -3385,7 +3395,7 @@ def _add_resilience_specs(specs: List[CommandSpec]) -> None: "AC_cas_get", "Flow", "Optimistic: Get", fields=( FieldSpec("name", FieldType.STRING, placeholder="config"), - FieldSpec("key", FieldType.STRING, placeholder="db.host"), + FieldSpec("key", FieldType.STRING, placeholder=_DOTTED_KEY_PLACEHOLDER), ), description="Read a versioned record {value, version}.", )) @@ -3621,21 +3631,21 @@ def _add_input_macro_specs(specs: List[CommandSpec]) -> None: def _add_screen_state_specs(specs: List[CommandSpec]) -> None: app = FieldSpec("app_name", FieldType.STRING, optional=True) specs.append(CommandSpec( - "AC_screen_snapshot", "Native UI", "Screen: Snapshot Baseline", + "AC_screen_snapshot", _NATIVE_UI, "Screen: Snapshot Baseline", fields=(app,), description="Snapshot the a11y tree as a semantic-diff baseline.", )) specs.append(CommandSpec( - "AC_screen_diff", "Native UI", "Screen: Diff Snapshots", + "AC_screen_diff", _NATIVE_UI, "Screen: Diff Snapshots", description="Semantic diff of 'before'/'after' snapshots (JSON view).", )) specs.append(CommandSpec( - "AC_screen_changed", "Native UI", "Screen: What Changed", + "AC_screen_changed", _NATIVE_UI, "Screen: What Changed", fields=(app,), description="Diff the live screen against the last snapshot baseline.", )) specs.append(CommandSpec( - "AC_describe_screen", "Native UI", "Screen: Describe", + "AC_describe_screen", _NATIVE_UI, "Screen: Describe", fields=(app,), description="Structured 'where am I' (role counts + control labels).", )) @@ -3643,7 +3653,7 @@ def _add_screen_state_specs(specs: List[CommandSpec]) -> None: def _add_set_of_marks_specs(specs: List[CommandSpec]) -> None: specs.append(CommandSpec( - "AC_cua_command", "Native UI", "Computer-Use: Map Action", + "AC_cua_command", _NATIVE_UI, "Computer-Use: Map Action", fields=( FieldSpec("payload", FieldType.STRING, placeholder='{"action":"left_click","coordinate":[x,y]}'), @@ -3653,43 +3663,43 @@ def _add_set_of_marks_specs(specs: List[CommandSpec]) -> None: description="Map an Anthropic / OpenAI computer-use action to an AC command.", )) specs.append(CommandSpec( - "AC_serialize_observation", "Native UI", "Observation: Serialize Elements", + "AC_serialize_observation", _NATIVE_UI, "Observation: Serialize Elements", fields=( FieldSpec("elements", FieldType.STRING, - placeholder='[{"role":"button","name":"OK","x":..,"y":..}]'), + placeholder=_MARKS_JSON_PLACEHOLDER), FieldSpec("viewport", FieldType.STRING, optional=True, - placeholder="[x, y, w, h]"), + placeholder=_RECT4_PLACEHOLDER), FieldSpec("max_elements", FieldType.INT, optional=True, default=80), ), description="Indexed text observation of UI elements for a VLM (act by index).", )) specs.append(CommandSpec( - "AC_observation_index", "Native UI", "Observation: Index Elements", + "AC_observation_index", _NATIVE_UI, "Observation: Index Elements", fields=( FieldSpec("elements", FieldType.STRING, - placeholder='[{"role":"button","name":"OK","x":..,"y":..}]'), + placeholder=_MARKS_JSON_PLACEHOLDER), FieldSpec("viewport", FieldType.STRING, optional=True, - placeholder="[x, y, w, h]"), + placeholder=_RECT4_PLACEHOLDER), FieldSpec("max_elements", FieldType.INT, optional=True, default=80), ), description="Reading-ordered, viewport-clipped, indexed element list.", )) specs.append(CommandSpec( - "AC_delta_observation", "Native UI", "Observation: Delta (what changed)", + "AC_delta_observation", _NATIVE_UI, "Observation: Delta (what changed)", fields=( FieldSpec("prev", FieldType.STRING, - placeholder='[{"role":"button","name":"OK","x":..,"y":..}]'), + placeholder=_MARKS_JSON_PLACEHOLDER), FieldSpec("curr", FieldType.STRING, - placeholder='[{"role":"button","name":"OK","x":..,"y":..}]'), + placeholder=_MARKS_JSON_PLACEHOLDER), FieldSpec("viewport", FieldType.STRING, optional=True, - placeholder="[x, y, w, h]"), + placeholder=_RECT4_PLACEHOLDER), FieldSpec("max_elements", FieldType.INT, optional=True, default=80), FieldSpec("max_lines", FieldType.INT, optional=True, default=40), ), description="Token-budgeted '+/~/-' summary of what changed between frames.", )) specs.append(CommandSpec( - "AC_classify_effect", "Native UI", "Classify Action Effect", + "AC_classify_effect", _NATIVE_UI, "Classify Action Effect", fields=( FieldSpec("before", FieldType.STRING, placeholder='[{"role":"button","name":"OK","x":0,"y":0}]'), @@ -3702,19 +3712,19 @@ def _add_set_of_marks_specs(specs: List[CommandSpec]) -> None: description="Did the action change the screen near its target? (no_op/…).", )) specs.append(CommandSpec( - "AC_effect_near_point", "Native UI", "Effect Near Point?", + "AC_effect_near_point", _NATIVE_UI, "Effect Near Point?", fields=( FieldSpec("before", FieldType.STRING, - placeholder='[{"role":"button","x":0,"y":0}]'), + placeholder=_BOXES_JSON_PLACEHOLDER), FieldSpec("after", FieldType.STRING, - placeholder='[{"role":"button","x":0,"y":0}]'), + placeholder=_BOXES_JSON_PLACEHOLDER), FieldSpec("point", FieldType.STRING, placeholder="[50, 50]"), FieldSpec("radius", FieldType.INT, optional=True, default=64), ), description="Did any before/after change land within radius of a point?", )) specs.append(CommandSpec( - "AC_check_postcondition", "Native UI", "Check Postcondition", + "AC_check_postcondition", _NATIVE_UI, "Check Postcondition", fields=( FieldSpec("after", FieldType.STRING, placeholder='[{"role":"dialog","name":"Saved"}]'), @@ -3727,7 +3737,7 @@ def _add_set_of_marks_specs(specs: List[CommandSpec]) -> None: description="Check expected outcome clauses against after/before frames.", )) specs.append(CommandSpec( - "AC_plan_repair", "Native UI", "Plan Repair Tactics", + "AC_plan_repair", _NATIVE_UI, "Plan Repair Tactics", fields=( FieldSpec("verdict", FieldType.STRING, placeholder="no_op / changed_elsewhere / changed"), @@ -3736,7 +3746,7 @@ def _add_set_of_marks_specs(specs: List[CommandSpec]) -> None: description="Ordered repair tactics for a failed/no-effect action verdict.", )) specs.append(CommandSpec( - "AC_consensus_point", "Native UI", "Grounding Consensus Point", + "AC_consensus_point", _NATIVE_UI, "Grounding Consensus Point", fields=( FieldSpec("candidates", FieldType.STRING, placeholder="[[100, 100], [104, 98], [97, 103]]"), @@ -3755,12 +3765,12 @@ def _add_set_of_marks_specs(specs: List[CommandSpec]) -> None: description="Index where a churn series first settles (offline settle check).", )) specs.append(CommandSpec( - "AC_build_critic_record", "Native UI", "Build Critic Record", + "AC_build_critic_record", _NATIVE_UI, "Build Critic Record", fields=( FieldSpec("action", FieldType.STRING, placeholder='{"type":"click","x":50,"y":50}'), FieldSpec("before", FieldType.STRING, - placeholder='[{"role":"button","x":0,"y":0}]'), + placeholder=_BOXES_JSON_PLACEHOLDER), FieldSpec("after", FieldType.STRING, placeholder='[{"role":"dialog","x":40,"y":40}]'), FieldSpec("postcondition", FieldType.STRING, optional=True, @@ -3770,7 +3780,7 @@ def _add_set_of_marks_specs(specs: List[CommandSpec]) -> None: description="Per-step critic evidence (effect + delta + postcondition).", )) specs.append(CommandSpec( - "AC_score_step", "Native UI", "Score Step (rule-based)", + "AC_score_step", _NATIVE_UI, "Score Step (rule-based)", fields=( FieldSpec("record", FieldType.STRING, placeholder='{"effect":{"effect":"changed_near_target"}}'), @@ -3778,45 +3788,45 @@ def _add_set_of_marks_specs(specs: List[CommandSpec]) -> None: description="Rule-based outcome + process score of a critic record.", )) specs.append(CommandSpec( - "AC_consensus_element", "Native UI", "Grounding Consensus Element", + "AC_consensus_element", _NATIVE_UI, "Grounding Consensus Element", fields=( FieldSpec("candidates", FieldType.STRING, placeholder="[[8, 8], [12, 10]]"), FieldSpec("elements", FieldType.STRING, - placeholder='[{"role":"button","x":0,"y":0}]'), + placeholder=_BOXES_JSON_PLACEHOLDER), ), description="Vote grounding proposals to the nearest element.", )) specs.append(CommandSpec( - "AC_validate_action", "Native UI", "Validate / Snap Action", + "AC_validate_action", _NATIVE_UI, "Validate / Snap Action", fields=( FieldSpec("action", FieldType.STRING, placeholder='{"type":"click","x":..,"y":..}'), FieldSpec("screen", FieldType.STRING, optional=True, placeholder="[width, height]"), FieldSpec("targets", FieldType.STRING, optional=True, - placeholder='[{"x":..,"y":..,"width":..,"height":..}]'), + placeholder=_POINTS_JSON_PLACEHOLDER), ), description="Reject out-of-bounds clicks; snap a near-miss to the nearest " "element.", )) specs.append(CommandSpec( - "AC_match_elements", "Native UI", "Match Elements (frames)", + "AC_match_elements", _NATIVE_UI, "Match Elements (frames)", fields=( FieldSpec("before", FieldType.STRING, - placeholder='[{"x":..,"y":..,"width":..,"height":..}]'), + placeholder=_POINTS_JSON_PLACEHOLDER), FieldSpec("after", FieldType.STRING, - placeholder='[{"x":..,"y":..,"width":..,"height":..}]'), + placeholder=_POINTS_JSON_PLACEHOLDER), FieldSpec("iou_threshold", FieldType.FLOAT, optional=True, default=0.5, min_value=0.0, max_value=1.0), ), description="Match element boxes across two frames by overlap (move/rename).", )) specs.append(CommandSpec( - "AC_assign_stable_ids", "Native UI", "Assign Stable Element IDs", + "AC_assign_stable_ids", _NATIVE_UI, "Assign Stable Element IDs", fields=( FieldSpec("elements", FieldType.STRING, - placeholder='[{"x":..,"y":..,"width":..,"height":..}]'), + placeholder=_POINTS_JSON_PLACEHOLDER), FieldSpec("prior", FieldType.STRING, optional=True, placeholder="prior frame's elements (with ids)"), FieldSpec("iou_threshold", FieldType.FLOAT, optional=True, default=0.5, @@ -3825,10 +3835,10 @@ def _add_set_of_marks_specs(specs: List[CommandSpec]) -> None: description="Tag elements with IDs carried across frames by overlap.", )) specs.append(CommandSpec( - "AC_score_candidates", "Native UI", "Score Candidates", + "AC_score_candidates", _NATIVE_UI, "Score Candidates", fields=( FieldSpec("candidates", FieldType.STRING, - placeholder='[{"role":"button","name":"OK","x":..,"y":..}]'), + placeholder=_MARKS_JSON_PLACEHOLDER), FieldSpec("want_role", FieldType.STRING, optional=True), FieldSpec("want_name", FieldType.STRING, optional=True), FieldSpec("anchor", FieldType.STRING, optional=True, @@ -3837,10 +3847,10 @@ def _add_set_of_marks_specs(specs: List[CommandSpec]) -> None: description="Rank candidate elements by role / name / proximity confidence.", )) specs.append(CommandSpec( - "AC_best_candidate", "Native UI", "Best Candidate", + "AC_best_candidate", _NATIVE_UI, "Best Candidate", fields=( FieldSpec("candidates", FieldType.STRING, - placeholder='[{"role":"button","name":"OK","x":..,"y":..}]'), + placeholder=_MARKS_JSON_PLACEHOLDER), FieldSpec("want_role", FieldType.STRING, optional=True), FieldSpec("want_name", FieldType.STRING, optional=True), FieldSpec("anchor", FieldType.STRING, optional=True, @@ -3849,7 +3859,7 @@ def _add_set_of_marks_specs(specs: List[CommandSpec]) -> None: description="The single highest-scoring candidate element.", )) specs.append(CommandSpec( - "AC_mark_screen", "Native UI", "Set-of-Marks: Number Elements", + "AC_mark_screen", _NATIVE_UI, "Set-of-Marks: Number Elements", fields=( FieldSpec("app_name", FieldType.STRING, optional=True), FieldSpec("render_path", FieldType.FILE_PATH, optional=True), @@ -3858,7 +3868,7 @@ def _add_set_of_marks_specs(specs: List[CommandSpec]) -> None: "grounding; optional numbered-box overlay screenshot.", )) specs.append(CommandSpec( - "AC_mark_click", "Native UI", "Set-of-Marks: Click Number", + "AC_mark_click", _NATIVE_UI, "Set-of-Marks: Click Number", fields=(FieldSpec("mark_id", FieldType.INT),), description="Click the element behind a numbered mark.", )) @@ -4085,7 +4095,7 @@ def _add_authoring_specs(specs: List[CommandSpec]) -> None: path = FieldSpec("path", FieldType.FILE_PATH) key = FieldSpec("key", FieldType.STRING) specs.append(CommandSpec( - "AC_element_save", "Native UI", "Element: Save Locator", + "AC_element_save", _NATIVE_UI, "Element: Save Locator", fields=(path, key, FieldSpec("name", FieldType.STRING, optional=True), FieldSpec("role", FieldType.STRING, optional=True), @@ -4093,22 +4103,22 @@ def _add_authoring_specs(specs: List[CommandSpec]) -> None: description="Save a named native-UI locator (object repository).", )) specs.append(CommandSpec( - "AC_element_find", "Native UI", "Element: Find Saved", + "AC_element_find", _NATIVE_UI, "Element: Find Saved", fields=(path, key), description="Resolve a saved locator to a live element summary.", )) specs.append(CommandSpec( - "AC_element_click", "Native UI", "Element: Click Saved", + "AC_element_click", _NATIVE_UI, "Element: Click Saved", fields=(path, key), description="Click the element behind a saved locator.", )) specs.append(CommandSpec( - "AC_element_remove", "Native UI", "Element: Remove Saved", + "AC_element_remove", _NATIVE_UI, "Element: Remove Saved", fields=(path, key), description="Delete a saved locator.", )) specs.append(CommandSpec( - "AC_element_list", "Native UI", "Element: List Saved", + "AC_element_list", _NATIVE_UI, "Element: List Saved", fields=(path,), description="List saved locator names in a repository file.", )) diff --git a/je_auto_control/utils/accessibility/backends/base.py b/je_auto_control/utils/accessibility/backends/base.py index 4b86ab54..3b1f6637 100644 --- a/je_auto_control/utils/accessibility/backends/base.py +++ b/je_auto_control/utils/accessibility/backends/base.py @@ -29,32 +29,32 @@ def get_value(self, name: Optional[str] = None, role: Optional[str] = None, app_name: Optional[str] = None, automation_id: Optional[str] = None) -> Optional[str]: """Return the matched control's value text, or None if not found.""" - self._unsupported("get_value") + self._unsupported("get_value", name, role, app_name, automation_id) def set_value(self, value: str, name: Optional[str] = None, role: Optional[str] = None, app_name: Optional[str] = None, automation_id: Optional[str] = None) -> bool: """Set the matched control's value; return True on success.""" - self._unsupported("set_value") + self._unsupported("set_value", value, name, role, app_name, automation_id) def invoke(self, name: Optional[str] = None, role: Optional[str] = None, app_name: Optional[str] = None, automation_id: Optional[str] = None) -> bool: """Invoke the matched control (e.g. press a button).""" - self._unsupported("invoke") + self._unsupported("invoke", name, role, app_name, automation_id) def toggle(self, name: Optional[str] = None, role: Optional[str] = None, app_name: Optional[str] = None, automation_id: Optional[str] = None) -> bool: """Toggle the matched control (e.g. a checkbox).""" - self._unsupported("toggle") + self._unsupported("toggle", name, role, app_name, automation_id) def read_table(self, name: Optional[str] = None, role: Optional[str] = None, app_name: Optional[str] = None, automation_id: Optional[str] = None, ) -> List[List[str]]: """Read a grid/table/list control as rows of cell strings.""" - self._unsupported("read_table") + self._unsupported("read_table", name, role, app_name, automation_id) # --- extended control patterns (Expand / Selection / Range / Scroll) ---- @@ -62,43 +62,43 @@ def expand(self, name: Optional[str] = None, role: Optional[str] = None, app_name: Optional[str] = None, automation_id: Optional[str] = None) -> bool: """Expand the matched control (ExpandCollapsePattern); True on success.""" - self._unsupported("expand") + self._unsupported("expand", name, role, app_name, automation_id) def collapse(self, name: Optional[str] = None, role: Optional[str] = None, app_name: Optional[str] = None, automation_id: Optional[str] = None) -> bool: """Collapse the matched control (ExpandCollapsePattern); True on success.""" - self._unsupported("collapse") + self._unsupported("collapse", name, role, app_name, automation_id) def expand_state(self, name: Optional[str] = None, role: Optional[str] = None, app_name: Optional[str] = None, automation_id: Optional[str] = None) -> Optional[str]: """Return ``expanded`` / ``collapsed`` / ``partial`` / ``leaf``, or None.""" - self._unsupported("expand_state") + self._unsupported("expand_state", name, role, app_name, automation_id) def select_item(self, name: Optional[str] = None, role: Optional[str] = None, app_name: Optional[str] = None, automation_id: Optional[str] = None) -> bool: """Select the matched item (SelectionItemPattern); True on success.""" - self._unsupported("select_item") + self._unsupported("select_item", name, role, app_name, automation_id) def get_range(self, name: Optional[str] = None, role: Optional[str] = None, app_name: Optional[str] = None, automation_id: Optional[str] = None) -> Optional[Dict[str, Any]]: """Return ``{value, minimum, maximum}`` (RangeValuePattern), or None.""" - self._unsupported("get_range") + self._unsupported("get_range", name, role, app_name, automation_id) def set_range_value(self, value: float, name: Optional[str] = None, role: Optional[str] = None, app_name: Optional[str] = None, automation_id: Optional[str] = None) -> bool: """Set a slider / progress value (RangeValuePattern); True on success.""" - self._unsupported("set_range_value") + self._unsupported("set_range_value", value, name, role, app_name, automation_id) def scroll_into_view(self, name: Optional[str] = None, role: Optional[str] = None, app_name: Optional[str] = None, automation_id: Optional[str] = None) -> bool: """Scroll the matched control into view (ScrollItemPattern); True on success.""" - self._unsupported("scroll_into_view") + self._unsupported("scroll_into_view", name, role, app_name, automation_id) # --- text patterns (TextPattern reads) --------------------------------- @@ -109,33 +109,33 @@ def document_text(self, name: Optional[str] = None, role: Optional[str] = None, Reads multiline / document controls where ValuePattern returns ``""``. """ - self._unsupported("document_text") + self._unsupported("document_text", name, role, app_name, automation_id) def selected_text(self, name: Optional[str] = None, role: Optional[str] = None, app_name: Optional[str] = None, automation_id: Optional[str] = None) -> Optional[str]: """Return the control's currently selected text (TextPattern), or None.""" - self._unsupported("selected_text") + self._unsupported("selected_text", name, role, app_name, automation_id) def visible_text(self, name: Optional[str] = None, role: Optional[str] = None, app_name: Optional[str] = None, automation_id: Optional[str] = None) -> Optional[str]: """Return only the on-screen text of the control (TextPattern), or None.""" - self._unsupported("visible_text") + self._unsupported("visible_text", name, role, app_name, automation_id) def find_text(self, text: str = "", ignore_case: bool = True, name: Optional[str] = None, role: Optional[str] = None, app_name: Optional[str] = None, automation_id: Optional[str] = None) -> bool: """Return whether ``text`` occurs in the control (TextPattern.FindText).""" - self._unsupported("find_text") + self._unsupported("find_text", text, ignore_case, name, role, app_name, automation_id) def select_text(self, text: str = "", ignore_case: bool = True, name: Optional[str] = None, role: Optional[str] = None, app_name: Optional[str] = None, automation_id: Optional[str] = None) -> bool: """Find ``text`` and select its range (TextPattern.FindText + Select).""" - self._unsupported("select_text") + self._unsupported("select_text", text, ignore_case, name, role, app_name, automation_id) def text_attributes(self, name: Optional[str] = None, role: Optional[str] = None, app_name: Optional[str] = None, @@ -143,7 +143,7 @@ def text_attributes(self, name: Optional[str] = None, ) -> Optional[Dict[str, Any]]: """Return formatting of the control's selection — ``{font_name, font_size, bold, italic, foreground_color}`` (TextPattern attributes), or None.""" - self._unsupported("text_attributes") + self._unsupported("text_attributes", name, role, app_name, automation_id) # --- keyboard focus ---------------------------------------------------- @@ -151,7 +151,7 @@ def set_focus(self, name: Optional[str] = None, role: Optional[str] = None, app_name: Optional[str] = None, automation_id: Optional[str] = None) -> bool: """Set keyboard focus on the matched control (SetFocus); True on success.""" - self._unsupported("set_focus") + self._unsupported("set_focus", name, role, app_name, automation_id) # --- virtualized items (realize off-screen list / grid items) ----------- @@ -168,7 +168,7 @@ def find_virtual_item(self, item_name: Optional[str] = None, by: str = "name", (``VirtualizedItemPattern``) so it exists as a real element. Returns the realized element, or None if the container or item isn't found. """ - self._unsupported("find_virtual_item") + self._unsupported("find_virtual_item", item_name, by, container_name, container_role, app_name, automation_id) # --- rich element properties ------------------------------------------- @@ -182,7 +182,7 @@ def get_properties(self, name: Optional[str] = None, ``enabled`` / ``offscreen`` / ``help_text`` / ``item_status`` / ``accelerator_key`` / ``access_key`` / ``orientation``. """ - self._unsupported("get_properties") + self._unsupported("get_properties", name, role, app_name, automation_id) # --- table headers + cell addressing (TablePattern / GridItemPattern) --- @@ -192,7 +192,7 @@ def get_table_headers(self, name: Optional[str] = None, automation_id: Optional[str] = None, ) -> Optional[Dict[str, Any]]: """Return a table's header labels as ``{columns: [...], rows: [...]}``.""" - self._unsupported("get_table_headers") + self._unsupported("get_table_headers", name, role, app_name, automation_id) def get_grid_cell(self, row: int = 0, column: int = 0, name: Optional[str] = None, role: Optional[str] = None, @@ -201,7 +201,7 @@ def get_grid_cell(self, row: int = 0, column: int = 0, ) -> Optional[Dict[str, Any]]: """Return the cell at ``(row, column)`` as ``{value, row, column, row_span, column_span}`` (GridPattern.GetItem + GridItemPattern).""" - self._unsupported("get_grid_cell") + self._unsupported("get_grid_cell", row, column, name, role, app_name, automation_id) # --- transform + window patterns (UIA-element-level) -------------------- @@ -210,21 +210,21 @@ def move_element(self, x: float = 0.0, y: float = 0.0, app_name: Optional[str] = None, automation_id: Optional[str] = None) -> bool: """Move the matched element to ``(x, y)`` (TransformPattern); True on success.""" - self._unsupported("move_element") + self._unsupported("move_element", x, y, name, role, app_name, automation_id) def resize_element(self, width: float = 0.0, height: float = 0.0, name: Optional[str] = None, role: Optional[str] = None, app_name: Optional[str] = None, automation_id: Optional[str] = None) -> bool: """Resize the matched element (TransformPattern); True on success.""" - self._unsupported("resize_element") + self._unsupported("resize_element", width, height, name, role, app_name, automation_id) def set_window_state(self, state: str = "normal", name: Optional[str] = None, role: Optional[str] = None, app_name: Optional[str] = None, automation_id: Optional[str] = None) -> bool: """Set a window's visual state ``normal`` / ``maximized`` / ``minimized``.""" - self._unsupported("set_window_state") + self._unsupported("set_window_state", state, name, role, app_name, automation_id) def window_interaction_state(self, name: Optional[str] = None, role: Optional[str] = None, @@ -233,7 +233,7 @@ def window_interaction_state(self, name: Optional[str] = None, ) -> Optional[str]: """Return a window's interaction state — ``ready`` / ``blocked_by_modal`` / ``not_responding`` / ``running`` / ``closing`` (WindowPattern), or None.""" - self._unsupported("window_interaction_state") + self._unsupported("window_interaction_state", name, role, app_name, automation_id) # --- MSAA bridge (LegacyIAccessiblePattern) ---------------------------- @@ -247,7 +247,7 @@ def legacy_info(self, name: Optional[str] = None, role: Optional[str] = None, last-resort read for legacy Win32 controls that expose nothing useful via the modern UIA patterns. """ - self._unsupported("legacy_info") + self._unsupported("legacy_info", name, role, app_name, automation_id) def legacy_default_action(self, name: Optional[str] = None, role: Optional[str] = None, @@ -255,7 +255,7 @@ def legacy_default_action(self, name: Optional[str] = None, automation_id: Optional[str] = None) -> bool: """Fire an old control's MSAA default action (DoDefaultAction); True on success — the fallback when Value / Invoke / Toggle all do nothing.""" - self._unsupported("legacy_default_action") + self._unsupported("legacy_default_action", name, role, app_name, automation_id) # --- container selection + views (Selection / MultipleView patterns) ---- @@ -265,7 +265,7 @@ def get_selection(self, name: Optional[str] = None, role: Optional[str] = None, ) -> Optional[Dict[str, Any]]: """Return a container's selection state — ``{items, can_select_multiple, is_required}`` (SelectionPattern), or None.""" - self._unsupported("get_selection") + self._unsupported("get_selection", name, role, app_name, automation_id) def list_views(self, name: Optional[str] = None, role: Optional[str] = None, app_name: Optional[str] = None, @@ -273,13 +273,13 @@ def list_views(self, name: Optional[str] = None, role: Optional[str] = None, ) -> Optional[Dict[str, Any]]: """Return a control's selectable views — ``{current, views: [...]}`` (MultipleViewPattern: list / details / tile / …), or None.""" - self._unsupported("list_views") + self._unsupported("list_views", name, role, app_name, automation_id) def set_view(self, view: str = "", name: Optional[str] = None, role: Optional[str] = None, app_name: Optional[str] = None, automation_id: Optional[str] = None) -> bool: """Switch a control to the named view (MultipleViewPattern); True on success.""" - self._unsupported("set_view") + self._unsupported("set_view", view, name, role, app_name, automation_id) # --- reactive events (UIA event subscription) -------------------------- @@ -291,9 +291,9 @@ def wait_for_focus_change(self, timeout: float = 5.0, A zero-latency native wait (UIA AddFocusChangedEventHandler) — unlike the polling recorder, it can't miss a fast focus transition. """ - self._unsupported("wait_for_focus_change") + self._unsupported("wait_for_focus_change", timeout) - def _unsupported(self, operation: str): + def _unsupported(self, operation: str, *context: Any): """Raise a clear error for an action this backend can't perform.""" raise AccessibilityNotAvailableError( f"{operation} is not supported by the {self.name} backend", diff --git a/je_auto_control/utils/assertion/assertions.py b/je_auto_control/utils/assertion/assertions.py index 2a80caee..f4214069 100644 --- a/je_auto_control/utils/assertion/assertions.py +++ b/je_auto_control/utils/assertion/assertions.py @@ -489,11 +489,12 @@ def assert_by_description(description: str, ) passed = (matched == present) state = "shows" if present else "does not show" + verdict = "match" if matched else "no match" message = ( f"assert_by_description passed: screen {state} {description!r}" if passed else f"assert_by_description failed: expected screen to {state} " - f"{description!r} (VLM verdict: {'match' if matched else 'no match'})" + f"{description!r} (VLM verdict: {verdict})" ) return _finalize( "vlm", passed, message, diff --git a/je_auto_control/utils/color_match/color_match.py b/je_auto_control/utils/color_match/color_match.py index a0858fc3..c7dd7cd0 100644 --- a/je_auto_control/utils/color_match/color_match.py +++ b/je_auto_control/utils/color_match/color_match.py @@ -26,8 +26,10 @@ def _hsv(source, region, is_haystack: bool): import cv2 - rgb = (_to_rgb(source) if source is not None - else _grab_rgb(region)) if is_haystack else _to_rgb(source) + if is_haystack: + rgb = _to_rgb(source) if source is not None else _grab_rgb(region) + else: + rgb = _to_rgb(source) return cv2.cvtColor(rgb, cv2.COLOR_RGB2HSV) diff --git a/je_auto_control/utils/config_bundle/__main__.py b/je_auto_control/utils/config_bundle/__main__.py index 880d4674..f57395a7 100644 --- a/je_auto_control/utils/config_bundle/__main__.py +++ b/je_auto_control/utils/config_bundle/__main__.py @@ -64,7 +64,8 @@ def _do_export(output: Path, root: Optional[Path]) -> int: def _do_import(source: Path, root: Optional[Path], dry_run: bool) -> int: try: - bundle = json.loads(source.read_text(encoding="utf-8")) + # source is an operator-supplied CLI path, not remote input + bundle = json.loads(source.read_text(encoding="utf-8")) # NOSONAR except (OSError, ValueError) as error: print(f"failed to read {source}: {error}", file=sys.stderr) return 2 diff --git a/je_auto_control/utils/element_scoring/element_scoring.py b/je_auto_control/utils/element_scoring/element_scoring.py index 4c84ddf2..4d614260 100644 --- a/je_auto_control/utils/element_scoring/element_scoring.py +++ b/je_auto_control/utils/element_scoring/element_scoring.py @@ -40,6 +40,26 @@ def _proximity(element: Element, anchor: Sequence[int]) -> float: return 1.0 / (1.0 + distance / 100.0) +def _signal_parts(element: Element, want_role: Optional[str], + want_name: Optional[str], + similarity: Callable[[str, str], float], + prefer_enabled: bool, + anchor: Optional[Sequence[int]]) -> Dict[str, float]: + """Build the per-signal 0..1 breakdown for one ``element``.""" + parts: Dict[str, float] = {} + if want_role is not None: + parts["role"] = (1.0 if str(element.get("role", "")).lower() + == str(want_role).lower() else 0.0) + if want_name is not None: + parts["name"] = float(similarity(want_name, + str(element.get("name", "")))) + if anchor is not None: + parts["proximity"] = _proximity(element, anchor) + if prefer_enabled: + parts["enabled"] = 1.0 if element.get("enabled", True) else 0.0 + return parts + + def score_candidates(candidates: Sequence[Element], *, want_role: Optional[str] = None, want_name: Optional[str] = None, @@ -57,17 +77,8 @@ def score_candidates(candidates: Sequence[Element], *, similarity = name_similarity or fuzzy_ratio scored: List[ScoredCandidate] = [] for element in candidates: - parts: Dict[str, float] = {} - if want_role is not None: - parts["role"] = (1.0 if str(element.get("role", "")).lower() - == str(want_role).lower() else 0.0) - if want_name is not None: - parts["name"] = float(similarity(want_name, - str(element.get("name", "")))) - if anchor is not None: - parts["proximity"] = _proximity(element, anchor) - if prefer_enabled: - parts["enabled"] = 1.0 if element.get("enabled", True) else 0.0 + parts = _signal_parts(element, want_role, want_name, similarity, + prefer_enabled, anchor) score = sum(parts.values()) / len(parts) if parts else 0.0 scored.append(ScoredCandidate(element, round(score, 4), parts)) scored.sort(key=lambda candidate: candidate.score, reverse=True) diff --git a/je_auto_control/utils/executor/action_executor.py b/je_auto_control/utils/executor/action_executor.py index 9f8cf9b3..0fcd3a21 100644 --- a/je_auto_control/utils/executor/action_executor.py +++ b/je_auto_control/utils/executor/action_executor.py @@ -171,6 +171,7 @@ def _run_dag(definition: Dict[str, Any], _AX_RECORDER_SINGLETON = None +_DEFAULT_APPROVALS_DIR = ".approvals" def _a11y_dump(app_name: Optional[str] = None, @@ -6029,7 +6030,7 @@ def _egress_reset() -> Dict[str, Any]: def _verify_artifact(name: str, content: Any, - approvals_dir: str = ".approvals", + approvals_dir: str = _DEFAULT_APPROVALS_DIR, extension: str = "txt") -> Dict[str, Any]: """Adapter: verify an artifact against its approved baseline.""" from je_auto_control.utils.approval import verify_artifact @@ -6039,14 +6040,14 @@ def _verify_artifact(name: str, content: Any, "received_path": result.received_path} -def _approve_artifact(name: str, approvals_dir: str = ".approvals", +def _approve_artifact(name: str, approvals_dir: str = _DEFAULT_APPROVALS_DIR, extension: str = "txt") -> Dict[str, Any]: """Adapter: promote a received artifact to the approved baseline.""" from je_auto_control.utils.approval import approve_artifact return {"approved": approve_artifact(name, approvals_dir, extension)} -def _pending_artifacts(approvals_dir: str = ".approvals") -> Dict[str, Any]: +def _pending_artifacts(approvals_dir: str = _DEFAULT_APPROVALS_DIR) -> Dict[str, Any]: """Adapter: list artifacts awaiting approval.""" from je_auto_control.utils.approval import pending_artifacts return {"pending": pending_artifacts(approvals_dir)} diff --git a/je_auto_control/utils/form_fields/form_fields.py b/je_auto_control/utils/form_fields/form_fields.py index 5fb646df..edb9eb3b 100644 --- a/je_auto_control/utils/form_fields/form_fields.py +++ b/je_auto_control/utils/form_fields/form_fields.py @@ -26,7 +26,7 @@ def _overlap_1d(a0: int, a1: int, b0: int, b1: int) -> int: def _right_value(label: Box, values: Sequence[Box], max_gap: int): """Nearest value to the right of ``label`` that shares a row, or ``None``.""" - left, top, right, bottom = _box_bounds(label) + _, top, right, bottom = _box_bounds(label) best: Optional[Tuple[Box, int]] = None for value in values: vl, vt, _, vb = _box_bounds(value) @@ -39,7 +39,7 @@ def _right_value(label: Box, values: Sequence[Box], max_gap: int): def _below_value(label: Box, values: Sequence[Box], max_gap: int): """Nearest value below ``label`` that shares a column, or ``None``.""" - left, top, right, bottom = _box_bounds(label) + left, _, right, bottom = _box_bounds(label) best: Optional[Tuple[Box, int]] = None for value in values: vl, vt, vr, _ = _box_bounds(value) diff --git a/je_auto_control/utils/governance/credential_broker.py b/je_auto_control/utils/governance/credential_broker.py index ef4dab72..4325a887 100644 --- a/je_auto_control/utils/governance/credential_broker.py +++ b/je_auto_control/utils/governance/credential_broker.py @@ -86,13 +86,16 @@ def active(self) -> List[Dict[str, object]]: """List non-expired leases as ``{token, name, ttl_remaining}`` (no values).""" now = self._clock() result: List[Dict[str, object]] = [] - for token, lease in list(self._leases.items()): + expired: List[str] = [] + for token, lease in self._leases.items(): remaining = float(lease["expires_at"]) - now if remaining > 0: result.append({"token": token, "name": lease["name"], "ttl_remaining": remaining}) else: - self._leases.pop(token, None) + expired.append(token) + for token in expired: + self._leases.pop(token, None) return result diff --git a/je_auto_control/utils/http_cassette/http_cassette.py b/je_auto_control/utils/http_cassette/http_cassette.py index 443c62cc..676cc30b 100644 --- a/je_auto_control/utils/http_cassette/http_cassette.py +++ b/je_auto_control/utils/http_cassette/http_cassette.py @@ -41,15 +41,9 @@ def _matches(recorded: Mapping[str, Any], call: Mapping[str, Any], match_on: Sequence[str]) -> bool: view = _request_view(call) for field in match_on: - if field == "method": - if recorded.get("method") != view["method"]: - return False - elif field == "url": - if recorded.get("url") != view["url"]: - return False - elif field == "body": - if recorded.get("body") != view["body"]: - return False + if field in ("method", "url", "body") and \ + recorded.get(field) != view[field]: + return False return True diff --git a/je_auto_control/utils/mcp_server/tools/_handlers.py b/je_auto_control/utils/mcp_server/tools/_handlers.py index 8069a557..4128e73d 100644 --- a/je_auto_control/utils/mcp_server/tools/_handlers.py +++ b/je_auto_control/utils/mcp_server/tools/_handlers.py @@ -12,6 +12,8 @@ from je_auto_control.utils.mcp_server.tools._base import MCPContent +_DEFAULT_APPROVALS_DIR = ".approvals" + # === Mouse / keyboard ======================================================= @@ -1515,7 +1517,7 @@ def egress_reset(): return {"allow": None, "deny": []} -def verify_artifact(name: str, content, approvals_dir: str = ".approvals", +def verify_artifact(name: str, content, approvals_dir: str = _DEFAULT_APPROVALS_DIR, extension: str = "txt"): from je_auto_control.utils.approval import verify_artifact as _verify result = _verify(name, content, approvals_dir, extension) @@ -1524,13 +1526,13 @@ def verify_artifact(name: str, content, approvals_dir: str = ".approvals", "received_path": result.received_path} -def approve_artifact(name: str, approvals_dir: str = ".approvals", +def approve_artifact(name: str, approvals_dir: str = _DEFAULT_APPROVALS_DIR, extension: str = "txt"): from je_auto_control.utils.approval import approve_artifact as _approve return {"approved": _approve(name, approvals_dir, extension)} -def pending_artifacts(approvals_dir: str = ".approvals"): +def pending_artifacts(approvals_dir: str = _DEFAULT_APPROVALS_DIR): from je_auto_control.utils.approval import pending_artifacts as _pending return {"pending": _pending(approvals_dir)} diff --git a/je_auto_control/utils/notify/notifier.py b/je_auto_control/utils/notify/notifier.py index df5e59ca..fbd0e83e 100644 --- a/je_auto_control/utils/notify/notifier.py +++ b/je_auto_control/utils/notify/notifier.py @@ -86,6 +86,6 @@ def notify(title: str, message: str, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) return NotifyResult(True, system, "sent") - except (FileNotFoundError, OSError, subprocess.SubprocessError) as error: + except (OSError, subprocess.SubprocessError) as error: autocontrol_logger.warning("notify failed: %r", error) return NotifyResult(False, system, repr(error)) diff --git a/je_auto_control/utils/remote_desktop/host_service.py b/je_auto_control/utils/remote_desktop/host_service.py index ee6e0782..ed58841a 100644 --- a/je_auto_control/utils/remote_desktop/host_service.py +++ b/je_auto_control/utils/remote_desktop/host_service.py @@ -217,7 +217,8 @@ def _interactive_configure() -> int: ) answers["poll_interval_s"] = 2.0 _DEFAULT_CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True) - _DEFAULT_CONFIG_PATH.write_text( + # _DEFAULT_CONFIG_PATH is a hardcoded module constant, not user input + _DEFAULT_CONFIG_PATH.write_text( # NOSONAR json.dumps(answers, indent=2), encoding="utf-8", ) try: diff --git a/je_auto_control/utils/remote_desktop/turn_config.py b/je_auto_control/utils/remote_desktop/turn_config.py index aa04f15c..a9373a32 100644 --- a/je_auto_control/utils/remote_desktop/turn_config.py +++ b/je_auto_control/utils/remote_desktop/turn_config.py @@ -142,7 +142,8 @@ def write_bundle(output_dir: Path, *, realm: str, user: str, secret: str, listen_port: int, tls_port: int, tls_cert: Optional[str], tls_key: Optional[str], external_ip: Optional[str]) -> None: - output_dir.mkdir(parents=True, exist_ok=True) + # output_dir is an operator-supplied CLI path, not remote input + output_dir.mkdir(parents=True, exist_ok=True) # NOSONAR conf_path = output_dir / "turnserver.conf" conf_path.write_text(render_turnserver_conf( realm=realm, listen_port=listen_port, tls_port=tls_port, diff --git a/je_auto_control/utils/remote_desktop/web_viewer/index.html b/je_auto_control/utils/remote_desktop/web_viewer/index.html index efadd0cc..f371c074 100644 --- a/je_auto_control/utils/remote_desktop/web_viewer/index.html +++ b/je_auto_control/utils/remote_desktop/web_viewer/index.html @@ -108,8 +108,8 @@ - - + Token kept in sessionStorage; cleared on tab close. diff --git a/je_auto_control/utils/stats/stats.py b/je_auto_control/utils/stats/stats.py index 08187a6c..4ace2f4e 100644 --- a/je_auto_control/utils/stats/stats.py +++ b/je_auto_control/utils/stats/stats.py @@ -88,11 +88,11 @@ def _betacf(a: float, b: float, x: float) -> float: def _betai(a: float, b: float, x: float) -> float: # x is a domain ratio in [0, 1]; both boundaries are reachable (e.g. x == 1 - # when the t-statistic is 0). NOSONAR: the analyzer mis-models these as - # constant, but they are genuine, exercised guards. - if x <= 0: # NOSONAR python:S2583 reason: reachable beta-domain lower bound + # when the t-statistic is 0); the analyzer mis-models these as constant, + # but they are genuine, exercised guards. + if x <= 0: # NOSONAR reachable beta-domain lower bound, not constant return 0.0 - if x >= 1: # NOSONAR python:S2583 reason: reachable beta-domain upper bound + if x >= 1: # NOSONAR reachable beta-domain upper bound, not constant return 1.0 front = math.exp(math.lgamma(a + b) - math.lgamma(a) - math.lgamma(b) + a * math.log(x) + b * math.log(1 - x)) diff --git a/je_auto_control/utils/stubs/generator.py b/je_auto_control/utils/stubs/generator.py index 8eaad392..fcec4444 100644 --- a/je_auto_control/utils/stubs/generator.py +++ b/je_auto_control/utils/stubs/generator.py @@ -88,7 +88,8 @@ def write_pyi(target: Path, target.parent.mkdir(parents=True, exist_ok=True) tmp = target.with_suffix(target.suffix + ".tmp") tmp.write_text(body, encoding="utf-8") - tmp.replace(target) + # target is an operator-supplied CLI path, not remote input + tmp.replace(target) # NOSONAR return target diff --git a/test/unit_test/headless/test_action_effect_batch.py b/test/unit_test/headless/test_action_effect_batch.py index cf691b73..694f1693 100644 --- a/test/unit_test/headless/test_action_effect_batch.py +++ b/test/unit_test/headless/test_action_effect_batch.py @@ -6,7 +6,7 @@ def _el(x, y, name="", role="button"): - return dict(x=x, y=y, width=40, height=20, role=role, name=name) + return {"x": x, "y": y, "width": 40, "height": 20, "role": role, "name": name} def test_no_op_when_nothing_changes(): diff --git a/test/unit_test/headless/test_canonical_log_batch.py b/test/unit_test/headless/test_canonical_log_batch.py index 87e30203..de55b766 100644 --- a/test/unit_test/headless/test_canonical_log_batch.py +++ b/test/unit_test/headless/test_canonical_log_batch.py @@ -20,6 +20,7 @@ def test_timer_uses_injected_clock(): ticks = iter([10.0, 10.5]) line = CanonicalLogLine(clock=lambda: next(ticks)) with line.timer("step"): + # body intentionally empty: the timer measures the enter->exit interval pass assert line.to_dict()["step_ms"] == pytest.approx(500.0) diff --git a/test/unit_test/headless/test_coordinate_space_batch.py b/test/unit_test/headless/test_coordinate_space_batch.py index 93ae65bb..0196276c 100644 --- a/test/unit_test/headless/test_coordinate_space_batch.py +++ b/test/unit_test/headless/test_coordinate_space_batch.py @@ -43,14 +43,14 @@ def test_clamping_is_in_bounds(): def test_downscale_png_matches_model_size(): - Image = pytest.importorskip("PIL.Image") + pil_image = pytest.importorskip("PIL.Image") import io buf = io.BytesIO() - Image.new("RGB", (640, 480), (1, 2, 3)).save(buf, format="PNG") + pil_image.new("RGB", (640, 480), (1, 2, 3)).save(buf, format="PNG") from je_auto_control.utils.coordinate_space import downscale_png space = normalized_space(640, 480, grid=64) out = downscale_png(buf.getvalue(), space) - with Image.open(io.BytesIO(out)) as resized: + with pil_image.open(io.BytesIO(out)) as resized: assert resized.size == (64, 64) diff --git a/test/unit_test/headless/test_critic_features_batch.py b/test/unit_test/headless/test_critic_features_batch.py index 7e0fbd12..daa565cc 100644 --- a/test/unit_test/headless/test_critic_features_batch.py +++ b/test/unit_test/headless/test_critic_features_batch.py @@ -6,7 +6,7 @@ def _el(x, y, name="", role="button"): - return dict(x=x, y=y, width=40, height=20, role=role, name=name) + return {"x": x, "y": y, "width": 40, "height": 20, "role": role, "name": name} def test_record_captures_effect_and_delta(): diff --git a/test/unit_test/headless/test_decision_table_batch.py b/test/unit_test/headless/test_decision_table_batch.py index 1bf92a52..f3a76acf 100644 --- a/test/unit_test/headless/test_decision_table_batch.py +++ b/test/unit_test/headless/test_decision_table_batch.py @@ -49,9 +49,9 @@ def test_unique_multi_match_raises(): def test_wildcard_and_literal_conditions(): spec = {"inputs": ["role", "active"], "hit_policy": "FIRST", "rules": [ - {"conditions": {"role": "admin", "active": None}, # active = wildcard + {"conditions": {"role": "admin", "active": None}, # active -> wildcard "outputs": {"allow": True}}, - {"conditions": {"role": "-", "active": True}, # role = wildcard + {"conditions": {"role": "-", "active": True}, # role -> wildcard "outputs": {"allow": False}}, ]} assert evaluate_table(spec, {"role": "admin", "active": False}) == \ diff --git a/test/unit_test/headless/test_diagnostics.py b/test/unit_test/headless/test_diagnostics.py index 0602ff40..2f207e35 100644 --- a/test/unit_test/headless/test_diagnostics.py +++ b/test/unit_test/headless/test_diagnostics.py @@ -43,7 +43,7 @@ def test_to_dict_payload_shape(): def test_cli_exits_zero_when_all_green(): """The CLI module should respect the runner's overall ``ok`` flag.""" - completed = subprocess.run( # noqa: S603 # local CLI test + completed = subprocess.run( # noqa: S603 # nosemgrep # local CLI test [sys.executable, "-m", "je_auto_control.utils.diagnostics"], capture_output=True, text=True, timeout=30, check=False, ) diff --git a/test/unit_test/headless/test_dotenv_batch.py b/test/unit_test/headless/test_dotenv_batch.py index a659d9fa..4b76bafa 100644 --- a/test/unit_test/headless/test_dotenv_batch.py +++ b/test/unit_test/headless/test_dotenv_batch.py @@ -44,7 +44,7 @@ def test_load_dotenv_file(tmp_path): path.write_text(_TEXT, encoding="utf-8") env = {"PLAIN": "keep"} load_dotenv(str(path), env) - assert env["PLAIN"] == "keep" and env["TOKEN"] == "secret" + assert env["PLAIN"] == "keep" and env["TOKEN"] == "secret" # NOSONAR load_dotenv mutates env in place load_dotenv(str(path), env, override=True) assert env["PLAIN"] == "hello" # override replaces assert dotenv_values(str(path))["TOKEN"] == "secret" diff --git a/test/unit_test/headless/test_image_dedup_batch.py b/test/unit_test/headless/test_image_dedup_batch.py index efa16619..32b08be3 100644 --- a/test/unit_test/headless/test_image_dedup_batch.py +++ b/test/unit_test/headless/test_image_dedup_batch.py @@ -10,7 +10,7 @@ def test_hamming_distance_and_similar(): assert hamming_distance("00", "00") == 0 - assert hamming_distance("0f", "00") == 4 # 0x0f = 1111 + assert hamming_distance("0f", "00") == 4 # 0x0f -> 1111 bits assert images_similar("ff", "fe", max_distance=1) is True assert images_similar("ff", "f0", max_distance=1) is False @@ -34,11 +34,11 @@ def test_dedupe_empty(): def test_real_pillow_hashing(tmp_path): - Image = pytest.importorskip("PIL.Image") + pil_image = pytest.importorskip("PIL.Image") black = tmp_path / "black.png" white = tmp_path / "white.png" - Image.new("RGB", (64, 64), (0, 0, 0)).save(black) - Image.new("RGB", (64, 64), (255, 255, 255)).save(white) + pil_image.new("RGB", (64, 64), (0, 0, 0)).save(black) + pil_image.new("RGB", (64, 64), (255, 255, 255)).save(white) h_black = average_hash(str(black)) assert isinstance(h_black, str) and h_black @@ -54,11 +54,11 @@ def test_real_pillow_hashing(tmp_path): # --- wiring --------------------------------------------------------------- def test_executor_round_trip(tmp_path): - Image = pytest.importorskip("PIL.Image") + pil_image = pytest.importorskip("PIL.Image") a = tmp_path / "a.png" b = tmp_path / "b.png" - Image.new("RGB", (32, 32), (10, 10, 10)).save(a) - Image.new("RGB", (32, 32), (10, 10, 10)).save(b) # identical + pil_image.new("RGB", (32, 32), (10, 10, 10)).save(a) + pil_image.new("RGB", (32, 32), (10, 10, 10)).save(b) # identical rec = ac.execute_action([ ["AC_dedupe_images", {"paths": [str(a), str(b)], "max_distance": 2}], ]) diff --git a/test/unit_test/headless/test_link_header_batch.py b/test/unit_test/headless/test_link_header_batch.py index ef93cf00..30b3b213 100644 --- a/test/unit_test/headless/test_link_header_batch.py +++ b/test/unit_test/headless/test_link_header_batch.py @@ -46,7 +46,7 @@ def test_paginate_follows_next_over_injected_fetch(): } responses = paginate("u1", lambda url: pages[url]) assert len(responses) == 3 - assert responses is not None and responses[-1]["headers"] == {} + assert responses[-1]["headers"] == {} def test_paginate_respects_max_pages(): diff --git a/test/unit_test/headless/test_remote_desktop_cursor.py b/test/unit_test/headless/test_remote_desktop_cursor.py index 715da144..d5412a0d 100644 --- a/test/unit_test/headless/test_remote_desktop_cursor.py +++ b/test/unit_test/headless/test_remote_desktop_cursor.py @@ -137,7 +137,8 @@ def test_frame_display_paints_cursor_overlay(): os.environ.setdefault("QT_QPA_PLATFORM", "offscreen") pytest.importorskip("PySide6.QtWidgets") from PySide6.QtWidgets import QApplication - QApplication.instance() or QApplication([]) + if QApplication.instance() is None: + QApplication([]) from je_auto_control.gui.remote_desktop.frame_display import _FrameDisplay display = _FrameDisplay() diff --git a/test/unit_test/headless/test_remote_desktop_websocket.py b/test/unit_test/headless/test_remote_desktop_websocket.py index 935d6ad6..e4638cbd 100644 --- a/test/unit_test/headless/test_remote_desktop_websocket.py +++ b/test/unit_test/headless/test_remote_desktop_websocket.py @@ -121,7 +121,7 @@ def client_side(): sec_key = line.split(":", 1)[1].strip() import base64 import hashlib - accept = base64.b64encode(hashlib.sha1( # nosec B324 + accept = base64.b64encode(hashlib.sha1( # nosec B324 # nosemgrep (sec_key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").encode("ascii"), usedforsecurity=False, ).digest()).decode("ascii") diff --git a/test/unit_test/headless/test_s3_store_batch.py b/test/unit_test/headless/test_s3_store_batch.py index 8be38546..e10ad8bc 100644 --- a/test/unit_test/headless/test_s3_store_batch.py +++ b/test/unit_test/headless/test_s3_store_batch.py @@ -23,12 +23,12 @@ def upload_file(self, filename, bucket, key): def download_file(self, bucket, key, filename): Path(filename).write_bytes(self.objects[(bucket, key)]) - def list_objects_v2(self, Bucket, Prefix=""): + def list_objects_v2(self, Bucket, Prefix=""): # NOSONAR boto3 API names keys = [k for (b, k) in self.objects if b == Bucket and k.startswith(Prefix)] return {"Contents": [{"Key": k} for k in sorted(keys)]} - def delete_object(self, Bucket, Key): + def delete_object(self, Bucket, Key): # NOSONAR boto3 API names self.objects.pop((Bucket, Key), None) diff --git a/test/unit_test/headless/test_schema_compat_batch.py b/test/unit_test/headless/test_schema_compat_batch.py index 8c0c6922..270fc199 100644 --- a/test/unit_test/headless/test_schema_compat_batch.py +++ b/test/unit_test/headless/test_schema_compat_batch.py @@ -24,7 +24,7 @@ def test_added_required_breaks_backward(): "required": ["id", "email"]} assert is_backward_compatible(_BASE, new) is False assert is_forward_compatible(_BASE, new) is True - [change] = [c for c in check_compatibility(_BASE, new)["breaking"]] + [change] = list(check_compatibility(_BASE, new)["breaking"]) assert change["kind"] == "field_added" and change["path"] == "email" diff --git a/test/unit_test/headless/test_timeseries_batch.py b/test/unit_test/headless/test_timeseries_batch.py index fd1a4ac7..b6233924 100644 --- a/test/unit_test/headless/test_timeseries_batch.py +++ b/test/unit_test/headless/test_timeseries_batch.py @@ -18,7 +18,7 @@ def test_rate_and_increase(): def test_rate_handles_counter_reset(): series = [(0, 90), (10, 100), (20, 5), (30, 25)] # reset between 100 and 5 - # increase = (100-90) + reset(5) + (25-5) = 10 + 5 + 20 = 35 + # increase: (100-90) + reset(5) + (25-5) -> 10 + 5 + 20 -> 35 assert ts_increase(series) == pytest.approx(35.0) assert ts_rate(series) == pytest.approx(35.0 / 30) diff --git a/test/unit_test/headless/test_window_capture.py b/test/unit_test/headless/test_window_capture.py index 22eaaea3..e17b2bc7 100644 --- a/test/unit_test/headless/test_window_capture.py +++ b/test/unit_test/headless/test_window_capture.py @@ -89,8 +89,12 @@ def mover(title, x, y, width, height): def test_snap_window_right_half(): rects = [] - snap_window("E", "right", screen_size=lambda: (1000, 800), - mover=lambda t, x, y, w, h: rects.append((x, y, w, h)) is None) + + def mover(_title, x, y, w, h): + rects.append((x, y, w, h)) + return 1 + + snap_window("E", "right", screen_size=lambda: (1000, 800), mover=mover) assert rects == [(500, 0, 500, 800)]