mirror of
https://github.com/Alishahryar1/free-claude-code.git
synced 2026-04-28 03:20:01 +00:00
ci: enhance type checking in workflow and improve test coverage
- Added a step to fail the CI if any '# type: ignore' comments are found in Python files. - Refactored tests to use mocking for better isolation and reliability. - Updated type hints and casting in several files to improve type safety.
This commit is contained in:
parent
97217debfa
commit
0d292cd578
6 changed files with 41 additions and 12 deletions
8
.github/workflows/tests.yml
vendored
8
.github/workflows/tests.yml
vendored
|
|
@ -33,6 +33,14 @@ jobs:
|
|||
- name: Type check
|
||||
run: uv run ty check
|
||||
|
||||
- name: Fail on type: ignore (no suppressions allowed)
|
||||
run: |
|
||||
if grep -rE '# type: ignore|# ty: ignore' --include='*.py' . --exclude-dir=.venv --exclude-dir=.git; then
|
||||
echo "::error::type: ignore / ty: ignore comments are not allowed. Fix the underlying type errors instead."
|
||||
exit 1
|
||||
fi
|
||||
exit 0
|
||||
|
||||
- name: Format
|
||||
run: uv run ruff format
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ Contains token counting for API requests.
|
|||
|
||||
import json
|
||||
import logging
|
||||
from typing import Any, List, Optional, Union
|
||||
from typing import Any, List, Optional, Union, cast
|
||||
|
||||
import tiktoken
|
||||
|
||||
|
|
@ -18,7 +18,7 @@ __all__ = ["get_token_count"]
|
|||
def _get_block_attr(block: object, key: str, default: Any = "") -> Any:
|
||||
"""Get attribute from block (object or dict)."""
|
||||
if isinstance(block, dict):
|
||||
return block.get(key, default) # type: ignore[no-matching-overload]
|
||||
return cast(dict[str, Any], block).get(key, default)
|
||||
return getattr(block, key, default)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from collections import deque
|
|||
from contextlib import asynccontextmanager
|
||||
from enum import Enum
|
||||
from datetime import datetime, timezone
|
||||
from typing import Dict, Optional, List, Any
|
||||
from typing import Dict, Optional, List, Any, cast
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from .models import IncomingMessage
|
||||
|
|
@ -265,7 +265,8 @@ class MessageTree:
|
|||
async with self._lock:
|
||||
# Read internal deque directly to avoid mutating queue state.
|
||||
# Drain/put approach would inflate _unfinished_tasks without task_done().
|
||||
return list(self._queue._queue) # type: ignore[attr-defined]
|
||||
queue_deque = cast(deque, getattr(self._queue, "_queue"))
|
||||
return list(queue_deque)
|
||||
|
||||
def get_queue_size(self) -> int:
|
||||
"""Get number of messages waiting in queue."""
|
||||
|
|
@ -281,10 +282,12 @@ class MessageTree:
|
|||
Note: asyncio.Queue has no built-in remove; we filter via the internal
|
||||
deque. O(n) in queue size; acceptable for typical tree queue sizes.
|
||||
"""
|
||||
queue_deque: deque = self._queue._queue # type: ignore[attr-defined]
|
||||
queue_deque = cast(deque, getattr(self._queue, "_queue"))
|
||||
if node_id not in queue_deque:
|
||||
return False
|
||||
self._queue._queue = deque(x for x in queue_deque if x != node_id) # type: ignore[attr-defined]
|
||||
object.__setattr__(
|
||||
self._queue, "_queue", deque(x for x in queue_deque if x != node_id)
|
||||
)
|
||||
return True
|
||||
|
||||
@asynccontextmanager
|
||||
|
|
|
|||
|
|
@ -193,4 +193,5 @@ class GlobalRateLimiter:
|
|||
self.set_blocked(delay)
|
||||
await asyncio.sleep(delay)
|
||||
|
||||
raise last_exc # type: ignore[misc]
|
||||
assert last_exc is not None
|
||||
raise last_exc
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ os.environ["PTB_TIMEDELTA"] = "1"
|
|||
# Add project root to path
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
||||
|
||||
from typing import Any
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
from providers.base import ProviderConfig
|
||||
from providers.nvidia_nim import NvidiaNimProvider
|
||||
|
|
@ -91,8 +92,22 @@ def mock_session_store():
|
|||
|
||||
@pytest.fixture
|
||||
def incoming_message_factory():
|
||||
_valid_keys = frozenset(
|
||||
{
|
||||
"text",
|
||||
"chat_id",
|
||||
"user_id",
|
||||
"message_id",
|
||||
"platform",
|
||||
"reply_to_message_id",
|
||||
"username",
|
||||
"timestamp",
|
||||
"raw_event",
|
||||
}
|
||||
)
|
||||
|
||||
def _create(**kwargs):
|
||||
defaults = {
|
||||
defaults: dict[str, Any] = {
|
||||
"text": "hello",
|
||||
"chat_id": "chat_1",
|
||||
"user_id": "user_1",
|
||||
|
|
@ -104,6 +119,7 @@ def incoming_message_factory():
|
|||
from datetime import datetime
|
||||
|
||||
defaults["timestamp"] = datetime.fromisoformat(defaults["timestamp"])
|
||||
return IncomingMessage(**defaults) # type: ignore
|
||||
filtered = {k: v for k, v in defaults.items() if k in _valid_keys}
|
||||
return IncomingMessage(**filtered)
|
||||
|
||||
return _create
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
from unittest.mock import patch
|
||||
|
||||
from messaging.transcript import TranscriptBuffer, RenderCtx
|
||||
from messaging.telegram_markdown import (
|
||||
escape_md_v2,
|
||||
|
|
@ -163,9 +165,8 @@ def test_transcript_render_segment_exception_skipped():
|
|||
def _raising_render(self, ctx):
|
||||
raise ValueError("render failed")
|
||||
|
||||
bad_segment.render = _raising_render # type: ignore[method-assign]
|
||||
|
||||
out = t.render(_ctx(), limit_chars=3900, status=None)
|
||||
with patch.object(bad_segment, "render", _raising_render):
|
||||
out = t.render(_ctx(), limit_chars=3900, status=None)
|
||||
assert "before" in out
|
||||
assert "after" in out
|
||||
assert "middle" not in out
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue