refactor: telegram modules and tighten linting (#111)
This commit is contained in:
+3
-3
@@ -14,7 +14,7 @@ from takopi.model import (
|
||||
|
||||
|
||||
def session_started(engine: str, value: str, title: str = "Codex") -> TakopiEvent:
|
||||
engine_id = EngineId(engine)
|
||||
engine_id: EngineId = engine
|
||||
return StartedEvent(
|
||||
engine=engine_id,
|
||||
resume=ResumeToken(engine=engine_id, value=value),
|
||||
@@ -29,7 +29,7 @@ def action_started(
|
||||
detail: dict[str, Any] | None = None,
|
||||
engine: str = "codex",
|
||||
) -> TakopiEvent:
|
||||
engine_id = EngineId(engine)
|
||||
engine_id: EngineId = engine
|
||||
return ActionEvent(
|
||||
engine=engine_id,
|
||||
action=Action(
|
||||
@@ -50,7 +50,7 @@ def action_completed(
|
||||
detail: dict[str, Any] | None = None,
|
||||
engine: str = "codex",
|
||||
) -> TakopiEvent:
|
||||
engine_id = EngineId(engine)
|
||||
engine_id: EngineId = engine
|
||||
return ActionEvent(
|
||||
engine=engine_id,
|
||||
action=Action(
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Callable, Iterable
|
||||
from typing import Any
|
||||
from collections.abc import Callable, Iterable
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
|
||||
@@ -5,7 +5,7 @@ import pytest
|
||||
|
||||
from takopi.runner_bridge import ExecBridgeConfig, IncomingMessage, handle_message
|
||||
from takopi.markdown import MarkdownParts, MarkdownPresenter
|
||||
from takopi.model import EngineId, ResumeToken, TakopiEvent
|
||||
from takopi.model import ResumeToken, TakopiEvent
|
||||
from takopi.telegram.render import prepare_telegram
|
||||
from takopi.runners.codex import CodexRunner
|
||||
from takopi.runners.mock import Advance, Emit, Raise, Return, ScriptRunner, Wait
|
||||
@@ -13,7 +13,7 @@ from takopi.settings import load_settings, require_telegram
|
||||
from takopi.transport import MessageRef, RenderedMessage, SendOptions
|
||||
from tests.factories import action_completed, action_started
|
||||
|
||||
CODEX_ENGINE = EngineId("codex")
|
||||
CODEX_ENGINE = "codex"
|
||||
|
||||
|
||||
class _FakeTransport:
|
||||
|
||||
@@ -7,14 +7,13 @@ from collections.abc import AsyncIterator
|
||||
from takopi.model import (
|
||||
ActionEvent,
|
||||
CompletedEvent,
|
||||
EngineId,
|
||||
ResumeToken,
|
||||
StartedEvent,
|
||||
TakopiEvent,
|
||||
)
|
||||
from takopi.runners.codex import CodexRunner, find_exec_only_flag
|
||||
|
||||
CODEX_ENGINE = EngineId("codex")
|
||||
CODEX_ENGINE = "codex"
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
|
||||
@@ -45,11 +45,10 @@ def test_resolve_default_base_prefers_master_over_main(monkeypatch) -> None:
|
||||
return None
|
||||
|
||||
def _fake_ok(args, **kwargs):
|
||||
if args == ["show-ref", "--verify", "--quiet", "refs/heads/master"]:
|
||||
return True
|
||||
if args == ["show-ref", "--verify", "--quiet", "refs/heads/main"]:
|
||||
return True
|
||||
return False
|
||||
return args in (
|
||||
["show-ref", "--verify", "--quiet", "refs/heads/master"],
|
||||
["show-ref", "--verify", "--quiet", "refs/heads/main"],
|
||||
)
|
||||
|
||||
monkeypatch.setattr("takopi.utils.git.git_stdout", _fake_stdout)
|
||||
monkeypatch.setattr("takopi.utils.git.git_ok", _fake_ok)
|
||||
|
||||
@@ -7,7 +7,6 @@ from takopi.model import (
|
||||
Action,
|
||||
ActionEvent,
|
||||
CompletedEvent,
|
||||
EngineId,
|
||||
ResumeToken,
|
||||
StartedEvent,
|
||||
TakopiEvent,
|
||||
@@ -15,7 +14,7 @@ from takopi.model import (
|
||||
from takopi.runners.mock import Emit, Return, ScriptRunner, Wait
|
||||
from tests.factories import action_started
|
||||
|
||||
CODEX_ENGINE = EngineId("codex")
|
||||
CODEX_ENGINE = "codex"
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
@@ -84,7 +83,7 @@ async def test_runner_releases_lock_when_consumer_closes() -> None:
|
||||
gate = anyio.Event()
|
||||
runner = ScriptRunner([Wait(gate)], engine=CODEX_ENGINE, resume_value="sid")
|
||||
|
||||
gen = cast(AsyncGenerator[TakopiEvent, None], runner.run("hello", None))
|
||||
gen = cast(AsyncGenerator[TakopiEvent], runner.run("hello", None))
|
||||
try:
|
||||
while True:
|
||||
evt = await anext(gen)
|
||||
@@ -94,7 +93,7 @@ async def test_runner_releases_lock_when_consumer_closes() -> None:
|
||||
await gen.aclose()
|
||||
|
||||
gen2 = cast(
|
||||
AsyncGenerator[TakopiEvent, None],
|
||||
AsyncGenerator[TakopiEvent],
|
||||
runner.run("again", ResumeToken(engine=CODEX_ENGINE, value="sid")),
|
||||
)
|
||||
try:
|
||||
|
||||
@@ -8,7 +8,6 @@ import takopi.runner as runner_module
|
||||
from takopi.model import (
|
||||
ActionEvent,
|
||||
CompletedEvent,
|
||||
EngineId,
|
||||
ResumeToken,
|
||||
StartedEvent,
|
||||
TakopiEvent,
|
||||
@@ -22,7 +21,7 @@ from takopi.runner import (
|
||||
|
||||
|
||||
class _DummyRunner(ResumeTokenMixin, BaseRunner):
|
||||
engine = EngineId("dummy")
|
||||
engine = "dummy"
|
||||
resume_re = re.compile(r"(?im)^`?dummy resume (?P<token>[^`\s]+)`?$")
|
||||
|
||||
async def run_impl(
|
||||
@@ -39,7 +38,7 @@ class _DummyRunner(ResumeTokenMixin, BaseRunner):
|
||||
|
||||
|
||||
class _DummyJsonlRunner(JsonlSubprocessRunner):
|
||||
engine = EngineId("dummy-jsonl")
|
||||
engine = "dummy-jsonl"
|
||||
|
||||
def command(self) -> str:
|
||||
return "dummy"
|
||||
@@ -67,7 +66,7 @@ class _DummyJsonlRunner(JsonlSubprocessRunner):
|
||||
|
||||
|
||||
class _BareJsonlRunner(JsonlSubprocessRunner):
|
||||
engine = EngineId("bare-jsonl")
|
||||
engine = "bare-jsonl"
|
||||
|
||||
|
||||
class _RunJsonlRunner(_DummyJsonlRunner):
|
||||
@@ -177,7 +176,7 @@ async def test_base_runner_run_locked_handles_resume() -> None:
|
||||
@pytest.mark.anyio
|
||||
async def test_base_runner_rejects_wrong_resume_engine() -> None:
|
||||
runner = _DummyRunner()
|
||||
bad_resume = ResumeToken(engine=EngineId("other"), value="oops")
|
||||
bad_resume = ResumeToken(engine="other", value="oops")
|
||||
with pytest.raises(RuntimeError):
|
||||
_ = [evt async for evt in runner.run("hello", bad_resume)]
|
||||
|
||||
@@ -185,7 +184,7 @@ async def test_base_runner_rejects_wrong_resume_engine() -> None:
|
||||
@pytest.mark.anyio
|
||||
async def test_base_runner_run_impl_not_implemented() -> None:
|
||||
class _BareRunner(BaseRunner):
|
||||
engine = EngineId("bare")
|
||||
engine = "bare"
|
||||
|
||||
runner = _BareRunner()
|
||||
with pytest.raises(NotImplementedError):
|
||||
@@ -204,7 +203,7 @@ def test_resume_token_format_and_extract() -> None:
|
||||
assert runner.extract_resume(None) is None
|
||||
|
||||
with pytest.raises(RuntimeError):
|
||||
runner.format_resume(ResumeToken(engine=EngineId("other"), value="bad"))
|
||||
runner.format_resume(ResumeToken(engine="other", value="bad"))
|
||||
|
||||
|
||||
def test_session_lock_reuse() -> None:
|
||||
@@ -294,7 +293,7 @@ def test_jsonl_helpers() -> None:
|
||||
assert found == resume
|
||||
assert emit is False
|
||||
|
||||
mismatch = StartedEvent(engine=EngineId("other"), resume=resume, title="t")
|
||||
mismatch = StartedEvent(engine="other", resume=resume, title="t")
|
||||
with pytest.raises(RuntimeError):
|
||||
runner.handle_started_event(mismatch, expected_session=None, found_session=None)
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ from typing import Any
|
||||
import pytest
|
||||
|
||||
from takopi.config import ProjectsConfig
|
||||
from takopi.model import EngineId
|
||||
from takopi.router import AutoRouter, RunnerEntry
|
||||
from takopi.runners.mock import Return, ScriptRunner
|
||||
from takopi.settings import (
|
||||
@@ -19,8 +18,8 @@ from takopi.transport_runtime import TransportRuntime
|
||||
|
||||
|
||||
def test_build_startup_message_includes_missing_engines(tmp_path: Path) -> None:
|
||||
codex = EngineId("codex")
|
||||
pi = EngineId("pi")
|
||||
codex = "codex"
|
||||
pi = "pi"
|
||||
runner = ScriptRunner([Return(answer="ok")], engine=codex)
|
||||
missing = ScriptRunner([Return(answer="ok")], engine=pi)
|
||||
router = AutoRouter(
|
||||
@@ -53,9 +52,9 @@ def test_build_startup_message_includes_missing_engines(tmp_path: Path) -> None:
|
||||
def test_build_startup_message_surfaces_unavailable_engine_reasons(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
codex = EngineId("codex")
|
||||
pi = EngineId("pi")
|
||||
claude = EngineId("claude")
|
||||
codex = "codex"
|
||||
pi = "pi"
|
||||
claude = "claude"
|
||||
runner = ScriptRunner([Return(answer="ok")], engine=codex)
|
||||
bad_cfg = ScriptRunner([Return(answer="ok")], engine=pi)
|
||||
load_err = ScriptRunner([Return(answer="ok")], engine=claude)
|
||||
@@ -100,7 +99,7 @@ def test_telegram_backend_build_and_run_wires_config(
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
codex = EngineId("codex")
|
||||
codex = "codex"
|
||||
runner = ScriptRunner([Return(answer="ok")], engine=codex)
|
||||
router = AutoRouter(
|
||||
entries=[RunnerEntry(engine=codex, runner=runner)],
|
||||
|
||||
@@ -6,8 +6,9 @@ import anyio
|
||||
import pytest
|
||||
|
||||
from takopi import commands, plugins
|
||||
from takopi.telegram.commands.executor import _CaptureTransport, _run_engine
|
||||
from takopi.telegram.commands.file_transfer import _handle_file_get, _handle_file_put
|
||||
import takopi.telegram.loop as telegram_loop
|
||||
import takopi.telegram.commands as telegram_commands
|
||||
import takopi.telegram.topics as telegram_topics
|
||||
from takopi.directives import parse_directives
|
||||
from takopi.telegram.api_models import (
|
||||
@@ -39,7 +40,7 @@ from takopi.context import RunContext
|
||||
from takopi.config import ProjectConfig, ProjectsConfig
|
||||
from takopi.runner_bridge import ExecBridgeConfig, RunningTask
|
||||
from takopi.markdown import MarkdownPresenter
|
||||
from takopi.model import EngineId, ResumeToken
|
||||
from takopi.model import ResumeToken
|
||||
from takopi.progress import ProgressTracker
|
||||
from takopi.router import AutoRouter, RunnerEntry
|
||||
from takopi.transport_runtime import TransportRuntime
|
||||
@@ -52,7 +53,7 @@ from takopi.telegram.types import (
|
||||
from takopi.transport import MessageRef, RenderedMessage, SendOptions
|
||||
from tests.plugin_fixtures import FakeEntryPoint, install_entrypoints
|
||||
|
||||
CODEX_ENGINE = EngineId("codex")
|
||||
CODEX_ENGINE = "codex"
|
||||
|
||||
|
||||
def _empty_projects() -> ProjectsConfig:
|
||||
@@ -905,9 +906,7 @@ async def test_handle_file_put_writes_file(tmp_path: Path) -> None:
|
||||
),
|
||||
)
|
||||
|
||||
await telegram_commands._handle_file_put(
|
||||
cfg, msg, "/proj uploads/hello.txt", None, None
|
||||
)
|
||||
await _handle_file_put(cfg, msg, "/proj uploads/hello.txt", None, None)
|
||||
|
||||
target = tmp_path / "uploads" / "hello.txt"
|
||||
assert target.read_bytes() == payload
|
||||
@@ -966,7 +965,7 @@ async def test_handle_file_get_sends_document_for_allowed_user(
|
||||
chat_type="supergroup",
|
||||
)
|
||||
|
||||
await telegram_commands._handle_file_get(cfg, msg, "/proj hello.txt", None, None)
|
||||
await _handle_file_get(cfg, msg, "/proj hello.txt", None, None)
|
||||
|
||||
assert bot.document_calls
|
||||
assert bot.document_calls[0]["filename"] == "hello.txt"
|
||||
@@ -1263,7 +1262,7 @@ async def test_send_with_resume_reports_when_missing() -> None:
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_run_engine_hides_resume_line_in_topics() -> None:
|
||||
transport = telegram_commands._CaptureTransport()
|
||||
transport = _CaptureTransport()
|
||||
runner = ScriptRunner(
|
||||
[Return(answer="ok")],
|
||||
engine=CODEX_ENGINE,
|
||||
@@ -1279,7 +1278,7 @@ async def test_run_engine_hides_resume_line_in_topics() -> None:
|
||||
projects=_empty_projects(),
|
||||
)
|
||||
|
||||
await telegram_commands._run_engine(
|
||||
await _run_engine(
|
||||
exec_cfg=exec_cfg,
|
||||
runtime=runtime,
|
||||
running_tasks={},
|
||||
@@ -1456,14 +1455,14 @@ async def test_run_main_loop_auto_resumes_topic_default_engine(
|
||||
123, 77, ResumeToken(engine=CODEX_ENGINE, value="resume-codex")
|
||||
)
|
||||
await store.set_session_resume(
|
||||
123, 77, ResumeToken(engine=EngineId("claude"), value="resume-claude")
|
||||
123, 77, ResumeToken(engine="claude", value="resume-claude")
|
||||
)
|
||||
await store.set_default_engine(123, 77, "claude")
|
||||
|
||||
transport = _FakeTransport()
|
||||
bot = _FakeBot()
|
||||
codex_runner = ScriptRunner([Return(answer="ok")], engine=CODEX_ENGINE)
|
||||
claude_runner = ScriptRunner([Return(answer="ok")], engine=EngineId("claude"))
|
||||
claude_runner = ScriptRunner([Return(answer="ok")], engine="claude")
|
||||
router = AutoRouter(
|
||||
entries=[
|
||||
RunnerEntry(engine=codex_runner.engine, runner=codex_runner),
|
||||
@@ -1521,7 +1520,7 @@ async def test_run_main_loop_auto_resumes_topic_default_engine(
|
||||
assert codex_runner.calls == []
|
||||
assert len(claude_runner.calls) == 1
|
||||
assert claude_runner.calls[0][1] == ResumeToken(
|
||||
engine=EngineId("claude"), value="resume-claude"
|
||||
engine="claude", value="resume-claude"
|
||||
)
|
||||
|
||||
|
||||
@@ -2443,7 +2442,7 @@ async def test_run_main_loop_command_uses_project_default_engine(
|
||||
transport = _FakeTransport()
|
||||
bot = _FakeBot()
|
||||
codex_runner = ScriptRunner([Return(answer="ok")], engine=CODEX_ENGINE)
|
||||
pi_runner = ScriptRunner([Return(answer="ok")], engine=EngineId("pi"))
|
||||
pi_runner = ScriptRunner([Return(answer="ok")], engine="pi")
|
||||
router = AutoRouter(
|
||||
entries=[
|
||||
RunnerEntry(engine=codex_runner.engine, runner=codex_runner),
|
||||
@@ -2525,7 +2524,7 @@ async def test_run_main_loop_command_defaults_to_chat_project(
|
||||
transport = _FakeTransport()
|
||||
bot = _FakeBot()
|
||||
codex_runner = ScriptRunner([Return(answer="ok")], engine=CODEX_ENGINE)
|
||||
pi_runner = ScriptRunner([Return(answer="ok")], engine=EngineId("pi"))
|
||||
pi_runner = ScriptRunner([Return(answer="ok")], engine="pi")
|
||||
router = AutoRouter(
|
||||
entries=[
|
||||
RunnerEntry(engine=codex_runner.engine, runner=codex_runner),
|
||||
|
||||
@@ -3,6 +3,7 @@ import pytest
|
||||
|
||||
from takopi.logging import setup_logging
|
||||
from takopi.telegram.client import TelegramClient, TelegramRetryAfter
|
||||
from takopi.telegram.client_api import HttpBotClient
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
@@ -25,9 +26,9 @@ async def test_telegram_429_no_retry() -> None:
|
||||
|
||||
client = httpx.AsyncClient(transport=transport)
|
||||
try:
|
||||
tg = TelegramClient("123:abcDEF_ghij", http_client=client)
|
||||
api = HttpBotClient("123:abcDEF_ghij", http_client=client)
|
||||
with pytest.raises(TelegramRetryAfter) as exc:
|
||||
await tg._post("sendMessage", {"chat_id": 1, "text": "hi"})
|
||||
await api._post("sendMessage", {"chat_id": 1, "text": "hi"})
|
||||
finally:
|
||||
await client.aclose()
|
||||
|
||||
@@ -49,8 +50,8 @@ async def test_no_token_in_logs_on_http_error(
|
||||
|
||||
client = httpx.AsyncClient(transport=transport)
|
||||
try:
|
||||
tg = TelegramClient(token, http_client=client)
|
||||
await tg._post("getUpdates", {"timeout": 1})
|
||||
api = HttpBotClient(token, http_client=client)
|
||||
await api._post("getUpdates", {"timeout": 1})
|
||||
finally:
|
||||
await client.aclose()
|
||||
|
||||
@@ -79,9 +80,9 @@ async def test_telegram_429_no_retry_post_form() -> None:
|
||||
|
||||
client = httpx.AsyncClient(transport=transport)
|
||||
try:
|
||||
tg = TelegramClient("123:abcDEF_ghij", http_client=client)
|
||||
api = HttpBotClient("123:abcDEF_ghij", http_client=client)
|
||||
with pytest.raises(TelegramRetryAfter) as exc:
|
||||
await tg._post_form(
|
||||
await api._post_form(
|
||||
"sendDocument",
|
||||
{"chat_id": 1},
|
||||
files={"document": ("note.txt", b"hi")},
|
||||
@@ -102,9 +103,9 @@ async def test_telegram_429_defaults_retry_after_on_bad_body() -> None:
|
||||
|
||||
client = httpx.AsyncClient(transport=transport)
|
||||
try:
|
||||
tg = TelegramClient("123:abcDEF_ghij", http_client=client)
|
||||
api = HttpBotClient("123:abcDEF_ghij", http_client=client)
|
||||
with pytest.raises(TelegramRetryAfter) as exc:
|
||||
await tg._post("sendMessage", {"chat_id": 1, "text": "hi"})
|
||||
await api._post("sendMessage", {"chat_id": 1, "text": "hi"})
|
||||
finally:
|
||||
await client.aclose()
|
||||
|
||||
@@ -124,8 +125,8 @@ async def test_telegram_ok_false_returns_none() -> None:
|
||||
|
||||
client = httpx.AsyncClient(transport=transport)
|
||||
try:
|
||||
tg = TelegramClient("123:abcDEF_ghij", http_client=client)
|
||||
result = await tg._post("getUpdates", {"timeout": 1})
|
||||
api = HttpBotClient("123:abcDEF_ghij", http_client=client)
|
||||
result = await api._post("getUpdates", {"timeout": 1})
|
||||
finally:
|
||||
await client.aclose()
|
||||
|
||||
@@ -141,8 +142,8 @@ async def test_telegram_invalid_payload_returns_none() -> None:
|
||||
|
||||
client = httpx.AsyncClient(transport=transport)
|
||||
try:
|
||||
tg = TelegramClient("123:abcDEF_ghij", http_client=client)
|
||||
result = await tg._post("getUpdates", {"timeout": 1})
|
||||
api = HttpBotClient("123:abcDEF_ghij", http_client=client)
|
||||
result = await api._post("getUpdates", {"timeout": 1})
|
||||
finally:
|
||||
await client.aclose()
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import pytest
|
||||
|
||||
from takopi.config import ProjectConfig, ProjectsConfig
|
||||
from takopi.context import RunContext
|
||||
from takopi.model import EngineId
|
||||
from takopi.router import AutoRouter, RunnerEntry
|
||||
from takopi.runners.mock import Return, ScriptRunner
|
||||
from takopi.telegram.chat_prefs import ChatPrefsStore
|
||||
@@ -15,8 +14,8 @@ from takopi.transport_runtime import TransportRuntime
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_resolve_engine_for_message_sources(tmp_path) -> None:
|
||||
codex = ScriptRunner([Return(answer="ok")], engine=EngineId("codex"))
|
||||
pi = ScriptRunner([Return(answer="ok")], engine=EngineId("pi"))
|
||||
codex = ScriptRunner([Return(answer="ok")], engine="codex")
|
||||
pi = ScriptRunner([Return(answer="ok")], engine="pi")
|
||||
router = AutoRouter(
|
||||
entries=[
|
||||
RunnerEntry(engine=codex.engine, runner=codex),
|
||||
@@ -42,7 +41,7 @@ async def test_resolve_engine_for_message_sources(tmp_path) -> None:
|
||||
resolved = await resolve_engine_for_message(
|
||||
runtime=runtime,
|
||||
context=RunContext(project="proj"),
|
||||
explicit_engine=EngineId("codex"),
|
||||
explicit_engine="codex",
|
||||
chat_id=1,
|
||||
topic_key=(1, 10),
|
||||
topic_store=topic_store,
|
||||
|
||||
@@ -15,8 +15,8 @@ class DummyTransport:
|
||||
def interactive_setup(self, *, force: bool) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
def lock_token(self, *, transport_config: object, config_path):
|
||||
_ = transport_config, config_path
|
||||
def lock_token(self, *, transport_config: object, _config_path):
|
||||
_ = transport_config, _config_path
|
||||
raise NotImplementedError
|
||||
|
||||
def build_and_run(
|
||||
|
||||
Reference in New Issue
Block a user