chore(fix): Resolve regressions in history logic, track startup, and GUI performance
This commit is contained in:
+3
-3
@@ -157,7 +157,7 @@ def build_file_items(base_dir: Path, files: list[str | dict[str, Any]]) -> list[
|
|||||||
tier = entry_raw.get("tier")
|
tier = entry_raw.get("tier")
|
||||||
auto_aggregate = entry_raw.get("auto_aggregate", True)
|
auto_aggregate = entry_raw.get("auto_aggregate", True)
|
||||||
force_full = entry_raw.get("force_full", False)
|
force_full = entry_raw.get("force_full", False)
|
||||||
view_mode = entry_raw.get("view_mode", "summary")
|
view_mode = entry_raw.get("view_mode", "full")
|
||||||
if force_full:
|
if force_full:
|
||||||
view_mode = "full"
|
view_mode = "full"
|
||||||
ast_signatures = entry_raw.get("ast_signatures", False)
|
ast_signatures = entry_raw.get("ast_signatures", False)
|
||||||
@@ -169,7 +169,7 @@ def build_file_items(base_dir: Path, files: list[str | dict[str, Any]]) -> list[
|
|||||||
tier = getattr(entry_raw, "tier", None)
|
tier = getattr(entry_raw, "tier", None)
|
||||||
auto_aggregate = getattr(entry_raw, "auto_aggregate", True)
|
auto_aggregate = getattr(entry_raw, "auto_aggregate", True)
|
||||||
force_full = getattr(entry_raw, "force_full", False)
|
force_full = getattr(entry_raw, "force_full", False)
|
||||||
view_mode = getattr(entry_raw, "view_mode", "summary")
|
view_mode = getattr(entry_raw, "view_mode", "full")
|
||||||
if force_full:
|
if force_full:
|
||||||
view_mode = "full"
|
view_mode = "full"
|
||||||
ast_signatures = getattr(entry_raw, "ast_signatures", False)
|
ast_signatures = getattr(entry_raw, "ast_signatures", False)
|
||||||
@@ -181,7 +181,7 @@ def build_file_items(base_dir: Path, files: list[str | dict[str, Any]]) -> list[
|
|||||||
tier = None
|
tier = None
|
||||||
auto_aggregate = True
|
auto_aggregate = True
|
||||||
force_full = False
|
force_full = False
|
||||||
view_mode = "summary"
|
view_mode = "full"
|
||||||
ast_signatures = False
|
ast_signatures = False
|
||||||
ast_definitions = False
|
ast_definitions = False
|
||||||
ast_mask = {}
|
ast_mask = {}
|
||||||
|
|||||||
+17
-3
@@ -2840,6 +2840,9 @@ class AppController:
|
|||||||
self._tool_stats.clear()
|
self._tool_stats.clear()
|
||||||
self._comms_log.clear()
|
self._comms_log.clear()
|
||||||
self.disc_entries.clear()
|
self.disc_entries.clear()
|
||||||
|
self.files.clear()
|
||||||
|
self.context_files.clear()
|
||||||
|
self.tracks.clear()
|
||||||
# Clear history in ALL discussions to be safe
|
# Clear history in ALL discussions to be safe
|
||||||
disc_sec = self.project.get("discussion", {})
|
disc_sec = self.project.get("discussion", {})
|
||||||
discussions = disc_sec.get("discussions", {})
|
discussions = disc_sec.get("discussions", {})
|
||||||
@@ -3205,14 +3208,15 @@ class AppController:
|
|||||||
try:
|
try:
|
||||||
# Use a local copy of files to avoid concurrent modification issues
|
# Use a local copy of files to avoid concurrent modification issues
|
||||||
files_to_scan = list(self.files)
|
files_to_scan = list(self.files)
|
||||||
for i, file_path in enumerate(files_to_scan):
|
for i, f_item in enumerate(files_to_scan):
|
||||||
try:
|
try:
|
||||||
self.ai_status = f"Phase 2: Scanning files ({i+1}/{len(files_to_scan)})..."
|
self.ai_status = f"Phase 2: Scanning files ({i+1}/{len(files_to_scan)})..."
|
||||||
abs_path = Path(self.active_project_root) / file_path
|
f_path = f_item.path if hasattr(f_item, 'path') else str(f_item)
|
||||||
|
abs_path = Path(self.active_project_root) / f_path
|
||||||
if abs_path.exists() and abs_path.suffix == ".py":
|
if abs_path.exists() and abs_path.suffix == ".py":
|
||||||
with open(abs_path, "r", encoding="utf-8") as f:
|
with open(abs_path, "r", encoding="utf-8") as f:
|
||||||
code = f.read()
|
code = f.read()
|
||||||
generated_skeletons += f"\nFile: {file_path}\n{parser.get_skeleton(code)}\n"
|
generated_skeletons += f"\nFile: {f_path}\n{parser.get_skeleton(code)}\n"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
pass
|
pass
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -3220,10 +3224,12 @@ class AppController:
|
|||||||
return # Exit if skeleton generation fails
|
return # Exit if skeleton generation fails
|
||||||
# Now loop through tracks and call _start_track_logic with generated skeletons
|
# Now loop through tracks and call _start_track_logic with generated skeletons
|
||||||
total_tracks = len(self.proposed_tracks)
|
total_tracks = len(self.proposed_tracks)
|
||||||
|
print(f"[DEBUG] _cb_accept_tracks: Starting {total_tracks} tracks...")
|
||||||
for i, track_data in enumerate(self.proposed_tracks):
|
for i, track_data in enumerate(self.proposed_tracks):
|
||||||
title = track_data.get("title") or track_data.get("goal", "Untitled Track")
|
title = track_data.get("title") or track_data.get("goal", "Untitled Track")
|
||||||
self.ai_status = f"Processing track {i+1} of {total_tracks}: '{title}'..."
|
self.ai_status = f"Processing track {i+1} of {total_tracks}: '{title}'..."
|
||||||
self._start_track_logic(track_data, skeletons_str=generated_skeletons) # Pass skeletons
|
self._start_track_logic(track_data, skeletons_str=generated_skeletons) # Pass skeletons
|
||||||
|
print(f"[DEBUG] _cb_accept_tracks: All {total_tracks} tracks processed.")
|
||||||
with self._pending_gui_tasks_lock:
|
with self._pending_gui_tasks_lock:
|
||||||
self._pending_gui_tasks.append({'action': 'refresh_from_project'}) # Ensure UI refresh after tracks are started
|
self._pending_gui_tasks.append({'action': 'refresh_from_project'}) # Ensure UI refresh after tracks are started
|
||||||
self.ai_status = f"All {total_tracks} tracks accepted and execution started."
|
self.ai_status = f"All {total_tracks} tracks accepted and execution started."
|
||||||
@@ -3327,15 +3333,23 @@ class AppController:
|
|||||||
with self._pending_gui_tasks_lock:
|
with self._pending_gui_tasks_lock:
|
||||||
self._pending_gui_tasks.append({'action': 'refresh_from_project'})
|
self._pending_gui_tasks.append({'action': 'refresh_from_project'})
|
||||||
# 4. Initialize ConductorEngine and run loop
|
# 4. Initialize ConductorEngine and run loop
|
||||||
|
sys.stderr.write(f"[DEBUG] _start_track_logic: Initializing engine for {track_id}...\n")
|
||||||
|
sys.stderr.flush()
|
||||||
engine = multi_agent_conductor.ConductorEngine(track, self.event_queue, auto_queue=not self.mma_step_mode)
|
engine = multi_agent_conductor.ConductorEngine(track, self.event_queue, auto_queue=not self.mma_step_mode)
|
||||||
self.engines[track.id] = engine
|
self.engines[track.id] = engine
|
||||||
# Use current full markdown context for the track execution
|
# Use current full markdown context for the track execution
|
||||||
track_id_param = track.id
|
track_id_param = track.id
|
||||||
flat = project_manager.flat_config(self.project, self.active_discussion, track_id=track_id_param)
|
flat = project_manager.flat_config(self.project, self.active_discussion, track_id=track_id_param)
|
||||||
flat.setdefault("files", {})["paths"] = self.context_files
|
flat.setdefault("files", {})["paths"] = self.context_files
|
||||||
|
sys.stderr.write(f"[DEBUG] _start_track_logic: Aggregating context for {track_id}...\n")
|
||||||
|
sys.stderr.flush()
|
||||||
full_md, _, _ = aggregate.run(flat)
|
full_md, _, _ = aggregate.run(flat)
|
||||||
|
sys.stderr.write(f"[DEBUG] _start_track_logic: Starting engine thread for {track_id}...\n")
|
||||||
|
sys.stderr.flush()
|
||||||
# Start the engine in a separate thread
|
# Start the engine in a separate thread
|
||||||
threading.Thread(target=engine.run, kwargs={"md_content": full_md}, daemon=True).start()
|
threading.Thread(target=engine.run, kwargs={"md_content": full_md}, daemon=True).start()
|
||||||
|
sys.stderr.write(f"[DEBUG] _start_track_logic: Engine thread spawned for {track_id}.\n")
|
||||||
|
sys.stderr.flush()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.ai_status = f"Track start error: {e}"
|
self.ai_status = f"Track start error: {e}"
|
||||||
print(f"ERROR in _start_track_logic: {e}")
|
print(f"ERROR in _start_track_logic: {e}")
|
||||||
|
|||||||
+51
-25
@@ -108,7 +108,8 @@ class App:
|
|||||||
"""
|
"""
|
||||||
self.controller = app_controller.AppController()
|
self.controller = app_controller.AppController()
|
||||||
self.controller._app = self
|
self.controller._app = self
|
||||||
from src import history
|
from src import history, performance_monitor
|
||||||
|
self.perf_monitor = performance_monitor.PerformanceMonitor()
|
||||||
self.history = history.HistoryManager(max_capacity=100)
|
self.history = history.HistoryManager(max_capacity=100)
|
||||||
self._last_ui_snapshot: Optional[history.UISnapshot] = None
|
self._last_ui_snapshot: Optional[history.UISnapshot] = None
|
||||||
self._snapshot_timer: float = 0.0
|
self._snapshot_timer: float = 0.0
|
||||||
@@ -117,7 +118,7 @@ class App:
|
|||||||
self._is_applying_snapshot: bool = False
|
self._is_applying_snapshot: bool = False
|
||||||
|
|
||||||
self.controller.init_state()
|
self.controller.init_state()
|
||||||
self.workspace_manager = workspace_manager.WorkspaceManager(project_root=self.active_project_root)
|
self.workspace_manager = workspace_manager.WorkspaceManager(project_root=self.controller.active_project_root)
|
||||||
self.workspace_profiles = self.workspace_manager.load_all_profiles()
|
self.workspace_profiles = self.workspace_manager.load_all_profiles()
|
||||||
self.show_windows.setdefault("Diagnostics", False)
|
self.show_windows.setdefault("Diagnostics", False)
|
||||||
self.controller.start_services(self)
|
self.controller.start_services(self)
|
||||||
@@ -133,7 +134,9 @@ class App:
|
|||||||
})
|
})
|
||||||
def simulate_save_preset(name: str):
|
def simulate_save_preset(name: str):
|
||||||
from src import models
|
from src import models
|
||||||
self.files = [models.FileItem(path='test.py')]
|
item = models.FileItem(path='test.py')
|
||||||
|
self.files = [item]
|
||||||
|
self.context_files = [item]
|
||||||
self.screenshots = ['test.png']
|
self.screenshots = ['test.png']
|
||||||
self.save_context_preset(name)
|
self.save_context_preset(name)
|
||||||
self.controller._predefined_callbacks['simulate_save_preset'] = simulate_save_preset
|
self.controller._predefined_callbacks['simulate_save_preset'] = simulate_save_preset
|
||||||
@@ -328,6 +331,7 @@ class App:
|
|||||||
auto_add_history=self.ui_auto_add_history,
|
auto_add_history=self.ui_auto_add_history,
|
||||||
disc_entries=copy.deepcopy(self.disc_entries),
|
disc_entries=copy.deepcopy(self.disc_entries),
|
||||||
files=[f.to_dict() if hasattr(f, 'to_dict') else f for f in self.files],
|
files=[f.to_dict() if hasattr(f, 'to_dict') else f for f in self.files],
|
||||||
|
context_files=[f.to_dict() if hasattr(f, 'to_dict') else f for f in self.context_files],
|
||||||
screenshots=list(self.screenshots)
|
screenshots=list(self.screenshots)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -354,6 +358,13 @@ class App:
|
|||||||
else:
|
else:
|
||||||
self.files.append(models.FileItem(path=str(f)))
|
self.files.append(models.FileItem(path=str(f)))
|
||||||
|
|
||||||
|
self.context_files = []
|
||||||
|
for f in snapshot.context_files:
|
||||||
|
if isinstance(f, dict):
|
||||||
|
self.context_files.append(models.FileItem.from_dict(f))
|
||||||
|
else:
|
||||||
|
self.context_files.append(models.FileItem(path=str(f)))
|
||||||
|
|
||||||
self.screenshots = list(snapshot.screenshots)
|
self.screenshots = list(snapshot.screenshots)
|
||||||
self._last_ui_snapshot = snapshot # Update last snapshot to avoid immediate re-push
|
self._last_ui_snapshot = snapshot # Update last snapshot to avoid immediate re-push
|
||||||
finally:
|
finally:
|
||||||
@@ -1639,25 +1650,13 @@ class App:
|
|||||||
if self.perf_profiling_enabled: self.perf_monitor.end_component("_gui_func")
|
if self.perf_profiling_enabled: self.perf_monitor.end_component("_gui_func")
|
||||||
|
|
||||||
def _handle_history_logic(self) -> None:
|
def _handle_history_logic(self) -> None:
|
||||||
# Skip history tracking only during active AI thinking/tool execution
|
"""
|
||||||
is_thinking = self.ai_status in ["sending...", "streaming...", "running powershell...", "fetching url...", "searching web..."]
|
Logic for capturing UI state for undo/redo.
|
||||||
if self._is_applying_snapshot or is_thinking:
|
"""
|
||||||
|
if self._is_applying_snapshot:
|
||||||
return
|
return
|
||||||
io = imgui.get_io()
|
|
||||||
|
|
||||||
# 1. Hotkey handling (Undo: Ctrl+Z, Redo: Ctrl+Y or Ctrl+Shift+Z)
|
|
||||||
ctrl = io.key_ctrl
|
|
||||||
shift = io.key_shift
|
|
||||||
|
|
||||||
if ctrl:
|
|
||||||
if imgui.is_key_pressed(imgui.Key.z):
|
|
||||||
if shift:
|
|
||||||
self._handle_redo()
|
|
||||||
else:
|
|
||||||
self._handle_undo()
|
|
||||||
elif imgui.is_key_pressed(imgui.Key.y):
|
|
||||||
self._handle_redo()
|
|
||||||
|
|
||||||
|
try:
|
||||||
# 2. Debounced snapshotting
|
# 2. Debounced snapshotting
|
||||||
current = self._take_snapshot()
|
current = self._take_snapshot()
|
||||||
if self._last_ui_snapshot is None:
|
if self._last_ui_snapshot is None:
|
||||||
@@ -1677,6 +1676,7 @@ class App:
|
|||||||
current.auto_add_history != self._last_ui_snapshot.auto_add_history or
|
current.auto_add_history != self._last_ui_snapshot.auto_add_history or
|
||||||
len(current.disc_entries) != len(self._last_ui_snapshot.disc_entries) or
|
len(current.disc_entries) != len(self._last_ui_snapshot.disc_entries) or
|
||||||
len(current.files) != len(self._last_ui_snapshot.files) or
|
len(current.files) != len(self._last_ui_snapshot.files) or
|
||||||
|
len(current.context_files) != len(self._last_ui_snapshot.context_files) or
|
||||||
len(current.screenshots) != len(self._last_ui_snapshot.screenshots)
|
len(current.screenshots) != len(self._last_ui_snapshot.screenshots)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1698,8 +1698,14 @@ class App:
|
|||||||
|
|
||||||
if self._pending_snapshot and (time.time() - self._snapshot_timer > self._snapshot_debounce):
|
if self._pending_snapshot and (time.time() - self._snapshot_timer > self._snapshot_debounce):
|
||||||
if self._state_to_push:
|
if self._state_to_push:
|
||||||
self.history.push(self._state_to_push, "UI State Change")
|
self.history.push(self._state_to_push, "UI Update")
|
||||||
|
self._state_to_push = None
|
||||||
self._pending_snapshot = False
|
self._pending_snapshot = False
|
||||||
|
except Exception as e:
|
||||||
|
import sys, traceback
|
||||||
|
sys.stderr.write(f"[DEBUG History] ERROR in _handle_history_logic: {e}\n")
|
||||||
|
traceback.print_exc(file=sys.stderr)
|
||||||
|
sys.stderr.flush()
|
||||||
|
|
||||||
def _render_base_prompt_diff_modal(self) -> None:
|
def _render_base_prompt_diff_modal(self) -> None:
|
||||||
if not getattr(self.controller, "_show_base_prompt_diff_modal", False):
|
if not getattr(self.controller, "_show_base_prompt_diff_modal", False):
|
||||||
@@ -2983,22 +2989,42 @@ class App:
|
|||||||
def _render_context_composition_panel(self) -> None:
|
def _render_context_composition_panel(self) -> None:
|
||||||
if not hasattr(self, '_file_stats_cache'):
|
if not hasattr(self, '_file_stats_cache'):
|
||||||
self._file_stats_cache = {}
|
self._file_stats_cache = {}
|
||||||
|
if not hasattr(self, '_file_stats_queue'):
|
||||||
|
self._file_stats_queue = []
|
||||||
|
if not hasattr(self, '_file_stats_worker_active'):
|
||||||
|
self._file_stats_worker_active = False
|
||||||
|
|
||||||
|
if imgui.collapsing_header("Context Composition"):
|
||||||
total_lines = 0
|
total_lines = 0
|
||||||
total_ast = 0
|
total_ast = 0
|
||||||
|
|
||||||
|
missing_keys = []
|
||||||
for f in self.context_files:
|
for f in self.context_files:
|
||||||
f_path = f.path if hasattr(f, "path") else str(f)
|
f_path = f.path if hasattr(f, "path") else str(f)
|
||||||
mtime = os.path.getmtime(f_path) if os.path.exists(f_path) else 0
|
mtime = os.path.getmtime(f_path) if os.path.exists(f_path) else 0
|
||||||
cache_key = f"{f_path}_{mtime}"
|
cache_key = f"{f_path}_{mtime}"
|
||||||
if cache_key not in self._file_stats_cache:
|
if cache_key not in self._file_stats_cache:
|
||||||
self._file_stats_cache[cache_key] = aggregate.compute_file_stats(f_path)
|
missing_keys.append((f_path, cache_key))
|
||||||
|
else:
|
||||||
stats = self._file_stats_cache[cache_key]
|
stats = self._file_stats_cache[cache_key]
|
||||||
total_lines += stats.get("lines", 0)
|
total_lines += stats.get("lines", 0)
|
||||||
total_ast += stats.get("ast_elements", 0)
|
total_ast += stats.get("ast_elements", 0)
|
||||||
|
|
||||||
if imgui.collapsing_header("Context Composition"):
|
# Process one missing key per frame or spawn a worker
|
||||||
#region: Batch Action Bar
|
if missing_keys and not self._file_stats_worker_active:
|
||||||
imgui.text("Batch:")
|
def _stats_worker():
|
||||||
|
self._file_stats_worker_active = True
|
||||||
|
try:
|
||||||
|
import threading
|
||||||
|
for path, key in missing_keys[:10]: # Process small batches
|
||||||
|
self._file_stats_cache[key] = aggregate.compute_file_stats(path)
|
||||||
|
finally:
|
||||||
|
self._file_stats_worker_active = False
|
||||||
|
|
||||||
|
import threading
|
||||||
|
threading.Thread(target=_stats_worker, daemon=True).start()
|
||||||
|
|
||||||
|
#region: Batch Action Bar imgui.text("Batch:")
|
||||||
imgui.same_line()
|
imgui.same_line()
|
||||||
for mode in ["full", "summary", "skeleton", "outline", "masked", "none"]:
|
for mode in ["full", "summary", "skeleton", "outline", "masked", "none"]:
|
||||||
if imgui.button(f"{mode.capitalize()}##batch"):
|
if imgui.button(f"{mode.capitalize()}##batch"):
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ class UISnapshot:
|
|||||||
auto_add_history: bool
|
auto_add_history: bool
|
||||||
disc_entries: list[dict]
|
disc_entries: list[dict]
|
||||||
files: list[dict]
|
files: list[dict]
|
||||||
|
context_files: list[dict]
|
||||||
screenshots: list[str]
|
screenshots: list[str]
|
||||||
|
|
||||||
def to_dict(self) -> dict:
|
def to_dict(self) -> dict:
|
||||||
@@ -34,6 +35,7 @@ class UISnapshot:
|
|||||||
"auto_add_history": self.auto_add_history,
|
"auto_add_history": self.auto_add_history,
|
||||||
"disc_entries": self.disc_entries,
|
"disc_entries": self.disc_entries,
|
||||||
"files": self.files,
|
"files": self.files,
|
||||||
|
"context_files": self.context_files,
|
||||||
"screenshots": self.screenshots
|
"screenshots": self.screenshots
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,6 +56,7 @@ class UISnapshot:
|
|||||||
auto_add_history=data.get("auto_add_history", False),
|
auto_add_history=data.get("auto_add_history", False),
|
||||||
disc_entries=data.get("disc_entries", []),
|
disc_entries=data.get("disc_entries", []),
|
||||||
files=data.get("files", []),
|
files=data.get("files", []),
|
||||||
|
context_files=data.get("context_files", []),
|
||||||
screenshots=data.get("screenshots", [])
|
screenshots=data.get("screenshots", [])
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -83,8 +83,8 @@ def test_view_mode_default_summary(tmp_path):
|
|||||||
test_file = base_dir / "test.py"
|
test_file = base_dir / "test.py"
|
||||||
test_file.write_text("def hello():\n print('world')\n", encoding="utf-8")
|
test_file.write_text("def hello():\n print('world')\n", encoding="utf-8")
|
||||||
|
|
||||||
# Test with simple string path
|
# Test with explicit summary mode
|
||||||
files = ["test.py"]
|
files = [{"path": "test.py", "view_mode": "summary"}]
|
||||||
items = aggregate.build_file_items(base_dir, files)
|
items = aggregate.build_file_items(base_dir, files)
|
||||||
|
|
||||||
assert len(items) == 1
|
assert len(items) == 1
|
||||||
|
|||||||
@@ -35,6 +35,10 @@ def test_mma_concurrent_tracks_execution(live_gui) -> None:
|
|||||||
client = api_hook_client.ApiHookClient()
|
client = api_hook_client.ApiHookClient()
|
||||||
assert client.wait_for_server(timeout=15), "Hook server did not start"
|
assert client.wait_for_server(timeout=15), "Hook server did not start"
|
||||||
|
|
||||||
|
# 0. Reset session to clear any stale state
|
||||||
|
client.click('btn_reset')
|
||||||
|
time.sleep(1.0)
|
||||||
|
|
||||||
# 1. Setup provider to custom mock
|
# 1. Setup provider to custom mock
|
||||||
mock_path = os.path.abspath("tests/mock_concurrent_mma.py")
|
mock_path = os.path.abspath("tests/mock_concurrent_mma.py")
|
||||||
client.set_value('current_provider', 'gemini_cli')
|
client.set_value('current_provider', 'gemini_cli')
|
||||||
|
|||||||
@@ -31,6 +31,10 @@ def test_mma_concurrent_tracks_stress(live_gui) -> None:
|
|||||||
client = api_hook_client.ApiHookClient()
|
client = api_hook_client.ApiHookClient()
|
||||||
assert client.wait_for_server(timeout=15), "Hook server did not start"
|
assert client.wait_for_server(timeout=15), "Hook server did not start"
|
||||||
|
|
||||||
|
# 0. Reset session to clear any stale state
|
||||||
|
client.click('btn_reset')
|
||||||
|
time.sleep(1.0)
|
||||||
|
|
||||||
# 1. Setup mock provider
|
# 1. Setup mock provider
|
||||||
client.set_value('current_provider', 'gemini_cli')
|
client.set_value('current_provider', 'gemini_cli')
|
||||||
client.set_value('gcli_path', f'"{sys.executable}" "{os.path.abspath("tests/mock_concurrent_mma.py")}"')
|
client.set_value('gcli_path', f'"{sys.executable}" "{os.path.abspath("tests/mock_concurrent_mma.py")}"')
|
||||||
|
|||||||
Reference in New Issue
Block a user