Skip to content

Commit 722014f

Browse files
committed
chore: add install command to all apps/packages and fix lint issues
1 parent f28a4c8 commit 722014f

31 files changed

+1373
-50
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ coverage/
4444
.coverage
4545
htmlcov/
4646
.pytest_cache/
47+
.ruff_cache/
4748

4849
# Terraform
4950
.terraform/

apps/api/mise.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
[tasks]
22
dev = { run = "uv run poe dev", description = "Start API server" }
3+
install = { run = "uv sync", description = "Install dependencies" }
34
lint = { run = "uv run poe lint", description = "Lint API" }
45
format = { run = "uv run poe format", description = "Format API" }
56
test = { run = "uv run poe test", description = "Test API" }
6-
typecheck = { run = "uv run poe type-check", description = "Type check API" }
7+
typecheck = { run = "uv run poe typecheck", description = "Type check API" }
78
migrate = { run = "uv run poe migrate", description = "Run DB migrations" }
89
"migrate:create" = { run = "uv run poe migrate-create", description = "Create migration" }
910
"gen:openapi" = { run = "uv run python scripts/gen_openapi.py", description = "Generate OpenAPI schema" }

apps/api/pyproject.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,10 @@ format = "ruff format ."
5353
check = ["lint", "format"]
5454

5555
# Type checking
56-
"type-check" = "mypy src"
56+
typecheck = "mypy src"
5757

5858
# All checks
59-
"all-checks" = ["check", "type-check"]
59+
"all-checks" = ["check", "typecheck"]
6060

6161
# Testing
6262
test = "pytest tests/"
@@ -76,3 +76,7 @@ exclude = ["alembic"]
7676
[[tool.mypy.overrides]]
7777
module = ["asyncpg.*"]
7878
ignore_missing_imports = true
79+
80+
[[tool.mypy.overrides]]
81+
module = ["redis.asyncio.*"]
82+
ignore_missing_imports = true

apps/api/src/common/models/pagination.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
"""Pagination models and utilities."""
22

3-
from typing import Generic, TypeVar
4-
53
from pydantic import BaseModel, Field
64

7-
T = TypeVar("T")
8-
95

106
class PaginationParams(BaseModel):
117
"""Query parameters for pagination."""
@@ -30,7 +26,7 @@ class PaginationMeta(BaseModel):
3026
has_prev: bool = Field(description="Whether there is a previous page")
3127

3228

33-
class PaginatedResponse(BaseModel, Generic[T]): # noqa: UP046
29+
class PaginatedResponse[T](BaseModel):
3430
"""Generic paginated response wrapper."""
3531

3632
data: list[T]

apps/api/src/lib/ai/base.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
from abc import ABC, abstractmethod
22
from collections.abc import AsyncIterator
3-
from typing import Any, Generic, TypeVar
3+
from typing import Any
44

5-
T = TypeVar("T")
65

7-
8-
class AIProvider(ABC, Generic[T]): # noqa: UP046
6+
class AIProvider[T](ABC):
97
@abstractmethod
108
async def analyze_image(self, image_data: bytes | list[bytes]) -> T:
119
pass

apps/api/src/lib/logging.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import logging
44
import sys
5-
from typing import Any
5+
from typing import Any, cast
66

77
import structlog
88
from opentelemetry import trace
@@ -72,4 +72,4 @@ def configure_logging() -> None:
7272

7373
def get_logger(name: str | None = None) -> structlog.stdlib.BoundLogger:
7474
"""Get a configured structured logger."""
75-
return structlog.get_logger(name)
75+
return cast(structlog.stdlib.BoundLogger, structlog.get_logger(name))

apps/api/src/lib/rate_limit.py

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,19 @@
22

33
import time
44
from collections import defaultdict
5-
from collections.abc import Callable
5+
from collections.abc import Awaitable, Callable
66
from dataclasses import dataclass, field
7+
from typing import TYPE_CHECKING, cast
78

89
from fastapi import HTTPException, Request, status
10+
from fastapi.responses import Response
911

1012
from src.lib.config import settings
1113
from src.lib.logging import get_logger
1214

15+
if TYPE_CHECKING:
16+
import redis.asyncio as redis_module
17+
1318
logger = get_logger(__name__)
1419

1520

@@ -74,14 +79,17 @@ class RedisRateLimiter:
7479
def __init__(self, requests: int, window: int):
7580
self.requests = requests
7681
self.window = window
77-
self._redis = None
82+
self._redis: "redis_module.Redis | None" = None
7883

79-
async def _get_redis(self):
84+
async def _get_redis(self) -> "redis_module.Redis":
8085
"""Lazy Redis connection."""
8186
if self._redis is None:
8287
import redis.asyncio as redis
8388

84-
self._redis = redis.from_url(settings.REDIS_URL)
89+
self._redis = cast(
90+
redis_module.Redis,
91+
redis.from_url(settings.REDIS_URL), # type: ignore[no-untyped-call]
92+
)
8593
return self._redis
8694

8795
async def is_allowed(self, key: str) -> tuple[bool, int, int]:
@@ -114,7 +122,7 @@ async def is_allowed(self, key: str) -> tuple[bool, int, int]:
114122

115123
return True, remaining, reset_after
116124

117-
async def close(self):
125+
async def close(self) -> None:
118126
"""Close Redis connection."""
119127
if self._redis:
120128
await self._redis.aclose()
@@ -144,7 +152,7 @@ def rate_limit(
144152
requests: int = 100,
145153
window: int = 60,
146154
key_func: Callable[[Request], str] | None = None,
147-
):
155+
) -> Callable[[Callable[..., Awaitable[Response]]], Callable[..., Awaitable[Response]]]:
148156
"""
149157
Rate limit decorator for FastAPI endpoints.
150158
@@ -162,16 +170,18 @@ async def get_resource():
162170
config = RateLimitConfig(requests=requests, window=window, key_func=key_func)
163171
actual_key_func = key_func or default_key_func
164172

165-
def decorator(func):
166-
async def wrapper(*args, **kwargs):
173+
def decorator(
174+
func: Callable[..., Awaitable[Response]],
175+
) -> Callable[..., Awaitable[Response]]:
176+
async def wrapper(*args: object, **kwargs: object) -> Response:
167177
# Find request in args/kwargs
168178
request: Request | None = None
169179
for arg in args:
170180
if isinstance(arg, Request):
171181
request = arg
172182
break
173183
if request is None:
174-
request = kwargs.get("request")
184+
request = cast(Request | None, kwargs.get("request"))
175185

176186
if request is None:
177187
return await func(*args, **kwargs)
@@ -208,8 +218,10 @@ async def wrapper(*args, **kwargs):
208218

209219

210220
async def rate_limit_middleware(
211-
request: Request, call_next, config: RateLimitConfig | None = None
212-
):
221+
request: Request,
222+
call_next: Callable[[Request], Awaitable[Response]],
223+
config: RateLimitConfig | None = None,
224+
) -> Response:
213225
"""
214226
Rate limit middleware for global application.
215227

apps/api/src/lib/telemetry.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import contextlib
44

5+
from fastapi import FastAPI
56
from opentelemetry import trace
67
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
78
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
@@ -42,7 +43,7 @@ def configure_telemetry() -> None:
4243
trace.set_tracer_provider(provider)
4344

4445

45-
def instrument_app(app: "FastAPI") -> None: # noqa: F821
46+
def instrument_app(app: FastAPI) -> None:
4647
"""Instrument FastAPI and other libraries for tracing."""
4748
from src.lib.database import engine
4849

apps/api/src/main.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
from collections.abc import AsyncIterator
1+
from collections.abc import AsyncIterator, Awaitable, Callable
22
from contextlib import asynccontextmanager
3-
from typing import Literal
3+
from typing import TYPE_CHECKING, Literal, cast
44
from uuid import uuid4
55

66
import structlog
7-
from fastapi import FastAPI, Request, status
7+
from fastapi import FastAPI, Request, Response, status
88
from fastapi.middleware.cors import CORSMiddleware
99
from fastapi.responses import JSONResponse
1010
from opentelemetry import trace
@@ -16,6 +16,9 @@
1616
from src.lib.logging import configure_logging, get_logger
1717
from src.lib.telemetry import configure_telemetry, instrument_app
1818

19+
if TYPE_CHECKING:
20+
import redis.asyncio as redis_module
21+
1922
# Configure logging first
2023
configure_logging()
2124
logger = get_logger(__name__)
@@ -44,7 +47,9 @@ async def lifespan(app: FastAPI) -> AsyncIterator[None]:
4447

4548
# Request ID middleware
4649
@app.middleware("http")
47-
async def request_id_middleware(request: Request, call_next):
50+
async def request_id_middleware(
51+
request: Request, call_next: Callable[[Request], Awaitable[Response]]
52+
) -> Response:
4853
"""Add request ID to each request for tracing."""
4954
request_id = request.headers.get("X-Request-ID", str(uuid4()))
5055

@@ -164,8 +169,8 @@ async def check_redis() -> ServiceStatus | None:
164169
try:
165170
import redis.asyncio as redis
166171

167-
client = redis.from_url(settings.REDIS_URL)
168-
await client.ping()
172+
client = cast(redis_module.Redis, redis.from_url(settings.REDIS_URL)) # type: ignore[no-untyped-call]
173+
await client.ping() # type: ignore[misc]
169174
await client.aclose()
170175
latency = (time.perf_counter() - start) * 1000
171176
return ServiceStatus(status="healthy", latency_ms=round(latency, 2))

apps/mobile/mise.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
[tasks]
22
dev = { run = "flutter run", description = "Run mobile app" }
33
build = { run = "flutter build", description = "Build mobile app" }
4+
install = { run = "flutter pub get", description = "Install dependencies" }
45
lint = { run = "flutter analyze", description = "Analyze mobile app" }
56
format = { run = "dart format .", description = "Format mobile app" }
67
test = { run = "flutter test", description = "Test mobile app" }

0 commit comments

Comments
 (0)