fix: repair imports lost in the 3.1 rebase and meet the new lint baseline

The rebase onto upstream 3.1 (mypy + ruff I001/F401 waves) left the tree
short of the newly enforced baselines in three ways:

* contextlib / UbridgeError imports dropped as false duplicates during
  conflict resolution left NameErrors in iou_vm and qemu_vm (F821);
  the Dynamips router grabbed the compute-level NIOTAP instead of the
  hypervisor one, breaking TAP anchoring (test failure).
* unused imports (KERNEL_UNSUPPORTED_FILTERS, UbridgeError, List) and
  unsorted blocks left behind where the stack's later commits moved the
  last usage elsewhere — ruff I001/F401/RUF022 now enforce these.
* mypy, now baseline-free: cache dict annotations in tc_probe, the
  locators candidate list, the resolved-params None branch in the MCP
  runner, and validate_bpf_syntax keeps the upstream Dict[str, Any]
  return (the strict Optional[str] was wrong for the "valid" bool entry).

The e2e raw-frame probe resolves its compiler via shutil.which (S607)
and zips with strict= (B905); the remaining subprocess call is S603 by
nature and gets a per-file ignore like other test files.

ruff check/format clean, mypy at upstream parity (no new errors),
full unit suite 2167 passed.
This commit is contained in:
YueGuobin committed 2026-10-06 00:21:11 +08:00
1 parent e13e5f3804
commit 7d04d08dba
35 files changed
+165 -136

No files matched your search

+4 -4
View File
@@ -346,13 +346,13 @@ def _run_handler_sync(handler, params: dict[str, Any]) -> list[dict[str, Any]]:
# Name-shaped project_id/node_id values become UUIDs before the handler
# runs; a name that matches nothing fails fast with the known names.
params, resolution_error = resolve_locators(params, run)
if resolution_error is not None:
resolved_params, resolution_error = resolve_locators(params, run)
if resolved_params is None:
result = resolution_error
else:
result = handler(params, ctx)
result = handler(resolved_params, ctx)
# backstop for UUID transcription slips: "Did you mean ...?"
result = add_did_you_mean(result, params, run)
result = add_did_you_mean(result, resolved_params, run)
return [{"type": "text", "text": json.dumps(result, ensure_ascii=False, default=str)}]
+9 -6
View File
@@ -37,9 +37,10 @@ distance 2 earns a "Did you mean ..." hint.
import re
from typing import Any, Callable
from .projects import list_projects_handler
from gns3server.agent.gns3_copilot.gns3_client.api_handlers import get_links_handler, get_nodes_handler
from .projects import list_projects_handler
_UUID_RE = re.compile(r"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$")
_ID_IN_TEXT_RE = re.compile(r"[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}")
@@ -94,9 +95,7 @@ def resolve_locators(
return params, None
def add_did_you_mean(
result: Any, params: dict[str, Any], run: Callable[[Callable, dict], Any]
) -> Any:
def add_did_you_mean(result: Any, params: dict[str, Any], run: Callable[[Callable, dict], Any]) -> Any:
"""
Append a "Did you mean ...?" hint to an error reply that names a
non-existent UUID, when exactly one in-scope id is within edit
@@ -118,7 +117,11 @@ def add_did_you_mean(
hints = []
for bad in bad_ids:
close = [(cid, label) for cid, label in candidates if bad != cid and _edit_distance_at_most(bad.lower(), cid.lower(), 2)]
close = [
(cid, label)
for cid, label in candidates
if bad != cid and _edit_distance_at_most(bad.lower(), cid.lower(), 2)
]
if len(close) == 1:
cid, label = close[0]
hints.append(f"Did you mean {label} ({cid})?")
@@ -177,7 +180,7 @@ def _collect_candidates(params: dict[str, Any], run: Callable[[Callable, dict],
just yields what was gathered so far.
"""
candidates = []
candidates: list[tuple[str, str]] = []
try:
projects = _as_items(run(list_projects_handler, {}), "projects") or []
candidates.extend((p["project_id"], f"project '{p.get('name', '?')}'") for p in projects if p.get("project_id"))