refactor: sms AIOHTTP_AVAILABLE flag; ElicitationHandler call_context defaults to a no-op thunk; drop stale TYPE_CHECKING/type-ignore in two tests
Self-review follow-ups on the F821 sweep: - plugins/platforms/sms/adapter.py: the optional-import block now sets AIOHTTP_AVAILABLE like the homeassistant / webhook / whatsapp_cloud adapters, and both call sites test the flag. Removes the `if not aiohttp is not None:` double negation left by inlining `_aiohttp_available()`. - tools/mcp_tool_sampling.py: `call_context` defaults to `lambda: None` so the use site is a single call instead of an Optional guard; the only None caller was a test. The `from __future__ import annotations` was noise (`Context` is a runtime import). Comment names the actual cycle (mcp_tool_server_run imports this module). - gateway/platforms/helpers.py: drop the `from __future__ import annotations` — the only MessageEvent annotations are attribute-target locals, which are never evaluated. - tests/gateway/test_telegram_audio_vs_voice.py, test_video_context_note.py: module-level `from gateway.run import GatewayRunner` like the ~100 sibling files; the TYPE_CHECKING block + `# type: ignore[name-defined]` were contradicting each other. (tests/e2e/conftest.py and test_feishu.py keep TYPE_CHECKING deliberately: they stub telegram/discord before importing, and FeishuAdapter is gated on optional lark_oapi.) Mutation check: neutralising the thunk read (`captured = None`) fails test_captured_context_is_replayed_in_consent_call; restored → 14/14 green. ty on the three touched production files vs origin/main: 0 new, 6 resolved.
This commit is contained in:
@@ -2,8 +2,6 @@
|
||||
stripping, thread participation tracking, GFM table → bullets, mention-pattern
|
||||
compilation, and fence-aware markdown chunking."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
|
||||
@@ -30,7 +30,9 @@ from gateway.platforms._shared import get_scoped_secret as _get_scoped_secret
|
||||
try:
|
||||
import aiohttp
|
||||
from aiohttp import web
|
||||
AIOHTTP_AVAILABLE = True
|
||||
except ImportError: # optional ([messaging] extra)
|
||||
AIOHTTP_AVAILABLE = False
|
||||
aiohttp = None # type: ignore[assignment]
|
||||
web = None # type: ignore[assignment]
|
||||
|
||||
@@ -75,7 +77,7 @@ def _new_session(**kwargs):
|
||||
|
||||
def check_sms_requirements() -> bool:
|
||||
"""Check if SMS adapter dependencies are available."""
|
||||
return aiohttp is not None and bool(
|
||||
return AIOHTTP_AVAILABLE and bool(
|
||||
_get_scoped_secret("TWILIO_ACCOUNT_SID") and _get_scoped_secret("TWILIO_AUTH_TOKEN"))
|
||||
|
||||
|
||||
@@ -292,7 +294,7 @@ def _strip_markdown_for_sms(message: str) -> str:
|
||||
async def _standalone_send(pconfig, chat_id, message, *, thread_id=None, media_files=None, force_document=False):
|
||||
"""Out-of-process SMS delivery via the Twilio REST API (standalone_sender_fn contract)."""
|
||||
auth_token = getattr(pconfig, "api_key", None) or _get_scoped_secret("TWILIO_AUTH_TOKEN", "")
|
||||
if not aiohttp is not None:
|
||||
if not AIOHTTP_AVAILABLE:
|
||||
return {"error": "aiohttp not installed. Run: pip install aiohttp"}
|
||||
account_sid = _get_scoped_secret("TWILIO_ACCOUNT_SID", "")
|
||||
from_number = os.getenv("TWILIO_PHONE_NUMBER", "")
|
||||
|
||||
@@ -18,17 +18,11 @@ import pytest
|
||||
|
||||
from gateway.config import GatewayConfig, Platform
|
||||
from gateway.platforms.event import MessageEvent, MessageType
|
||||
from gateway.run import GatewayRunner
|
||||
from gateway.session import SessionSource
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from gateway.run import GatewayRunner
|
||||
|
||||
|
||||
def _make_runner(stt_enabled: bool = True) -> "GatewayRunner": # type: ignore[name-defined]
|
||||
from gateway.run import GatewayRunner
|
||||
|
||||
def _make_runner(stt_enabled: bool = True) -> GatewayRunner:
|
||||
runner = GatewayRunner.__new__(GatewayRunner)
|
||||
runner.config = GatewayConfig(stt_enabled=stt_enabled)
|
||||
runner.adapters = {}
|
||||
|
||||
@@ -6,17 +6,11 @@ import pytest
|
||||
|
||||
from gateway.config import GatewayConfig, Platform
|
||||
from gateway.platforms.event import MessageEvent, MessageType
|
||||
from gateway.run import GatewayRunner
|
||||
from gateway.session import SessionSource
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from gateway.run import GatewayRunner
|
||||
|
||||
|
||||
def _make_runner() -> "GatewayRunner": # type: ignore[name-defined]
|
||||
from gateway.run import GatewayRunner
|
||||
|
||||
def _make_runner() -> GatewayRunner:
|
||||
runner = GatewayRunner.__new__(GatewayRunner)
|
||||
runner.config = GatewayConfig()
|
||||
runner.adapters = {}
|
||||
|
||||
@@ -254,7 +254,7 @@ class TestElicitationHandlerContextBridge:
|
||||
call) the handler must still invoke the consent router -- just
|
||||
without the contextvar replay. Otherwise CLI/TUI sessions, which
|
||||
don't set HERMES_SESSION_PLATFORM, would break."""
|
||||
handler = ElicitationHandler("pay", {"timeout": 5}, call_context=None)
|
||||
handler = ElicitationHandler("pay", {"timeout": 5})
|
||||
params = _form_params()
|
||||
|
||||
with patch("tools.approval_prompt.request_elicitation_consent", return_value="accept") as m:
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
"""MCP client-side handlers for server-initiated requests: sampling
|
||||
(sampling/createMessage, text and tool-use results) and elicitation."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import functools
|
||||
import json
|
||||
@@ -255,12 +253,13 @@ class ElicitationHandler:
|
||||
_ANSWER_RESULTS = {"accept": ("accept", "accepted"), "cancel": ("cancel", "errors")}
|
||||
|
||||
def __init__(self, server_name: str, config: dict,
|
||||
call_context: Optional[Callable[[], Optional[Context]]] = None):
|
||||
call_context: Callable[[], Optional[Context]] = lambda: None):
|
||||
self.server_name = server_name
|
||||
# 5 min mirrors the gateway approval default so async surfaces (Telegram, Slack) can respond.
|
||||
self.timeout = _safe_numeric(config.get("timeout", 300), 300, float)
|
||||
# Returns the owning MCPServerTask's contextvars snapshot for the in-flight tool call (None
|
||||
# between calls). A thunk, not the task: the task module imports this one.
|
||||
# between calls). A thunk, not the task: mcp_tool_server_run imports this module, so
|
||||
# MCPServerTask cannot be named here.
|
||||
self._call_context = call_context
|
||||
self.metrics = {"requests": 0, "accepted": 0, "declined": 0, "errors": 0}
|
||||
|
||||
@@ -281,7 +280,7 @@ class ElicitationHandler:
|
||||
|
||||
consent = functools.partial(request_elicitation_consent, message, description,
|
||||
timeout_seconds=int(self.timeout), surface=f"mcp-elicitation/{self.server_name}")
|
||||
captured = self._call_context() if self._call_context else None
|
||||
captured = self._call_context()
|
||||
return consent if captured is None else (lambda: captured.copy().run(consent))
|
||||
|
||||
async def __call__(self, context, params):
|
||||
|
||||
Reference in New Issue
Block a user