fix(hooks): handle dict-key bracket notation in set_value / get_value
The Hook API previously rejected key strings like 'show_windows["Project Settings"]' (and silently returned None on get). The test_live_gui_filedialog_regression test exercises exactly this pattern to open the Project Settings window via the Hook API; it was previously marked skip with "hook server doesn't handle the dict-key bracket-notation syntax". Fix in three small places: 1. src/app_controller.py:_handle_set_value If `item` is not in _settable_fields, try parsing it as `dict_name[<key>]` notation. If dict_name IS in _settable_fields and the current attr is a dict, set the inner key. 2. src/api_hooks.py:/api/gui/value (POST get_val) Mirror the parsing for the field-based get endpoint. 3. src/api_hook_client.py:ApiHookClient.get_value Mirror the parsing in the client so the dict-key syntax works through the state endpoint as well (which is what get_value actually calls by default). Test fix: - tests/test_live_gui_filedialog_regression.py: removed the @pytest.mark.skip marker; the underlying issue is now fixed. Verified: 1/1 test passes (previously skipped).
This commit is contained in:
@@ -161,6 +161,14 @@ class ApiHookClient:
|
||||
Gets the value of a GUI item via its mapped field.
|
||||
[C: simulation/sim_ai_settings.py:AISettingsSimulation.run, simulation/sim_base.py:BaseSimulation.get_value, simulation/sim_base.py:BaseSimulation.setup, simulation/sim_base.py:BaseSimulation.wait_for_element, simulation/sim_context.py:ContextSimulation.run, simulation/sim_execution.py:ExecutionSimulation.run, simulation/workflow_sim.py:WorkflowSimulator.run_discussion_turn_async, simulation/workflow_sim.py:WorkflowSimulator.wait_for_ai_response, tests/smoke_status_hook.py:test_status_hook, tests/smoke_status_hook.py:wait_for_value, tests/test_auto_switch_sim.py:test_auto_switch_sim, tests/test_deepseek_infra.py:test_gui_provider_list_via_hooks, tests/test_extended_sims.py:test_ai_settings_sim_live, tests/test_gui2_parity.py:test_gui2_click_hook_works, tests/test_gui2_parity.py:test_gui2_set_value_hook_works, tests/test_rag_phase4_final_verify.py:test_phase4_final_verify, tests/test_rag_phase4_stress.py:test_rag_large_codebase_verification_sim, tests/test_rag_visual_sim.py:test_rag_full_lifecycle_sim, tests/test_rag_visual_sim.py:test_rag_settings_persistence_sim, tests/test_selectable_ui.py:test_selectable_label_stability, tests/test_system_prompt_sim.py:test_system_prompt_sim, tests/test_undo_redo_sim.py:test_undo_redo_context_mutation, tests/test_undo_redo_sim.py:test_undo_redo_discussion_mutation, tests/test_undo_redo_sim.py:test_undo_redo_lifecycle, tests/test_workspace_profiles_sim.py:test_workspace_profiles_restoration]
|
||||
"""
|
||||
# Dict-key bracket notation: e.g. 'show_windows["Project Settings"]'
|
||||
if "[" in item and item.endswith("]"):
|
||||
dict_name, _, key_part = item.partition("[")
|
||||
key = key_part[:-1].strip().strip("'\"")
|
||||
state = self.get_gui_state()
|
||||
if isinstance(state.get(dict_name), dict) and key in state[dict_name]:
|
||||
return state[dict_name][key]
|
||||
return None
|
||||
# Try state endpoint first (new preferred way)
|
||||
state = self.get_gui_state()
|
||||
if item in state:
|
||||
|
||||
@@ -464,6 +464,14 @@ class HookHandler(BaseHTTPRequestHandler):
|
||||
if field_tag in settable:
|
||||
attr = settable[field_tag]
|
||||
result["value"] = _serialize_for_api(_get_app_attr(app, attr, None))
|
||||
elif "[" in field_tag and field_tag.endswith("]"):
|
||||
dict_name, _, key_part = field_tag.partition("[")
|
||||
key = key_part[:-1].strip().strip("'\"")
|
||||
if dict_name in settable:
|
||||
attr = settable[dict_name]
|
||||
current = _get_app_attr(app, attr, None)
|
||||
if isinstance(current, dict) and key in current:
|
||||
result["value"] = _serialize_for_api(current[key])
|
||||
finally: event.set()
|
||||
lock = _get_app_attr(app, "_pending_gui_tasks_lock")
|
||||
tasks = _get_app_attr(app, "_pending_gui_tasks")
|
||||
|
||||
@@ -560,6 +560,17 @@ def _handle_set_value(controller: 'AppController', task: dict):
|
||||
setattr(controller, attr_name, value)
|
||||
if item == "gcli_path":
|
||||
controller._update_gcli_adapter(str(value))
|
||||
return
|
||||
# Dict-key bracket notation: e.g. 'show_windows["Project Settings"]'
|
||||
if "[" in item and item.endswith("]"):
|
||||
dict_name, _, key_part = item.partition("[")
|
||||
key = key_part[:-1].strip().strip("'\"")
|
||||
if dict_name in controller._settable_fields:
|
||||
attr_name = controller._settable_fields[dict_name]
|
||||
current = getattr(controller, attr_name, None)
|
||||
if isinstance(current, dict):
|
||||
new_dict = {**current, key: value}
|
||||
setattr(controller, attr_name, new_dict)
|
||||
|
||||
def _handle_click(controller: 'AppController', task: dict):
|
||||
"""[SDM: AppController._handle_click]"""
|
||||
|
||||
@@ -16,7 +16,6 @@ import pytest
|
||||
from src.api_hook_client import ApiHookClient
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="Pre-existing test bug: client.set_value('show_windows[\"Project Settings\"]', True) returns None (the hook server doesn't handle the dict-key bracket-notation syntax in the key name). The same key written as 'show_windows.Project Settings' (or via _handle_set_value directly) would work. Tracked as pre-existing.")
|
||||
def test_live_gui_project_settings_opens_without_filedialog_crash(live_gui) -> None:
|
||||
"""
|
||||
Regression: the Project Settings window's render call chain ends
|
||||
|
||||
Reference in New Issue
Block a user