Skip to content

Commit

Permalink
Enforce mypy in CI, fix all type errors (#16)
Browse files Browse the repository at this point in the history
* Enforce mypy in CI, fix all type errors

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fix ver

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
cmyui and pre-commit-ci[bot] authored Jun 16, 2024
1 parent 44c09df commit cd0fb7b
Show file tree
Hide file tree
Showing 18 changed files with 93 additions and 48 deletions.
24 changes: 24 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Lint
on:
push:
branches:
- main
paths:
- "**.py"
pull_request:

jobs:
mypy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.10"
cache: "pip"
- name: Install dependencies
run: pip install -rrequirements{,-dev}.txt
- name: Run mypy
run: mypy .
Empty file added app/__init__.py
Empty file.
Empty file added app/api/__init__.py
Empty file.
21 changes: 16 additions & 5 deletions app/api/rest/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

from fastapi import FastAPI
from fastapi import Request
from fastapi import Response
from starlette.middleware.base import RequestResponseEndpoint

from app.common import settings
from app.services import database
Expand Down Expand Up @@ -54,7 +56,7 @@ async def startup_redis() -> None:
ssl=settings.REDIS_USE_SSL,
username=settings.REDIS_USER,
)
await service_redis.initialize()
await service_redis.initialize() # type: ignore[unused-awaitable]
api.state.redis = service_redis
logging.info("Redis pool started up")

Expand All @@ -68,19 +70,28 @@ async def shutdown_redis() -> None:

def init_middlewares(api: FastAPI) -> None:
@api.middleware("http")
async def add_db_to_request(request: Request, call_next):
async def add_db_to_request(
request: Request,
call_next: RequestResponseEndpoint,
) -> Response:
request.state.db = request.app.state.db
response = await call_next(request)
return response

@api.middleware("http")
async def add_redis_to_request(request: Request, call_next):
async def add_redis_to_request(
request: Request,
call_next: RequestResponseEndpoint,
) -> Response:
request.state.redis = request.app.state.redis
response = await call_next(request)
return response

@api.middleware("http")
async def add_process_time_header(request: Request, call_next):
async def add_process_time_header(
request: Request,
call_next: RequestResponseEndpoint,
) -> Response:
start_time = time.perf_counter_ns()
response = await call_next(request)
process_time = (time.perf_counter_ns() - start_time) / 1e6
Expand All @@ -94,7 +105,7 @@ def init_routes(api: FastAPI) -> None:
api.include_router(v1_router)


def init_api():
def init_api() -> FastAPI:
api = FastAPI()

init_db(api)
Expand Down
4 changes: 2 additions & 2 deletions app/api/rest/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ def __init__(self, request: Request) -> None:

@property
def db(self) -> database.Database:
return self.request.state.db
return self.request.state.db # type: ignore[no-any-return]

@property
def redis(self) -> redis.ServiceRedis:
return self.request.state.redis
return self.request.state.redis # type: ignore[no-any-return]
3 changes: 2 additions & 1 deletion app/api/rest/v1/pp.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from fastapi import APIRouter
from fastapi import Depends
from fastapi import Response

from app.api.rest.context import RequestContext
from app.common import responses
Expand All @@ -20,7 +21,7 @@ async def get_profile_pp_history(
user_id: int,
mode: int,
ctx: RequestContext = Depends(),
):
) -> Response:
data = await pp.fetch_many(ctx, user_id, mode)
user_data = await user.fetch_one(ctx, user_id, mode)

Expand Down
5 changes: 3 additions & 2 deletions app/api/rest/v1/rank.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from fastapi import APIRouter
from fastapi import Depends
from fastapi import Response

from app.api.rest.context import RequestContext
from app.common import responses
Expand All @@ -21,7 +22,7 @@ async def get_profile_rank_history(
user_id: int,
mode: int,
ctx: RequestContext = Depends(),
):
) -> Response:
data = await rank.fetch_many(ctx, user_id, mode)
user_data = await user.fetch_one(ctx, user_id, mode)

Expand Down Expand Up @@ -70,7 +71,7 @@ async def get_profile_peak_rank(
user_id: int,
mode: int,
ctx: RequestContext = Depends(),
):
) -> Response:
data = await rank.fetch_peak(ctx, user_id, mode)
user_data = await user.fetch_one(ctx, user_id, mode)

Expand Down
4 changes: 2 additions & 2 deletions app/common/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class Success(BaseModel, Generic[T]):
def success(
content: Any,
status_code: int = 200,
headers: dict | None = None,
headers: dict[str, str] | None = None,
) -> json.ORJSONResponse:
data = {"status": "success", "data": content}
return json.ORJSONResponse(data, status_code, headers)
Expand All @@ -37,7 +37,7 @@ def failure(
error: ServiceError,
message: str,
status_code: int = 400,
headers: dict | None = None,
headers: dict[str, str] | None = None,
) -> json.ORJSONResponse:
data = {"status": "error", "error": error, "message": message}
return json.ORJSONResponse(data, status_code, headers)
Empty file added app/models/__init__.py
Empty file.
2 changes: 1 addition & 1 deletion app/models/pp.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
class PPHistory(BaseModel):
user_id: int
mode: int
captures: list
captures: list[PPCapture]

@classmethod
def from_mapping(cls, mapping: Mapping[str, Any]) -> PPHistory:
Expand Down
Empty file added app/repositories/__init__.py
Empty file.
3 changes: 1 addition & 2 deletions app/repositories/pp.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from __future__ import annotations

from collections.abc import Mapping
from typing import Any

from app.common.context import Context
Expand All @@ -19,7 +18,7 @@ async def fetch_many(
user_id: int,
mode: int,
limit: int = 89,
) -> list[Mapping[str, Any]]:
) -> list[dict[str, Any]]:
query = f"""\
SELECT {self.READ_PARAMS}
FROM `user_profile_history`
Expand Down
5 changes: 2 additions & 3 deletions app/repositories/rank.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from __future__ import annotations

from collections.abc import Mapping
from typing import Any

from app.common.context import Context
Expand All @@ -22,7 +21,7 @@ async def fetch_peak(
self,
user_id: int,
mode: int,
) -> Mapping[str, Any] | None:
) -> dict[str, Any] | None:
query = f"""\
SELECT {self.READ_PEAK_PARAMS} FROM `user_profile_history`
WHERE `user_id` = :user_id AND `mode` = :mode AND `rank` > 0
Expand All @@ -39,7 +38,7 @@ async def fetch_many(
user_id: int,
mode: int,
limit: int = 89, # one will be added in api.
) -> list[Mapping[str, Any]]:
) -> list[dict[str, Any]]:
query = f"""\
SELECT {self.READ_PARAMS}
FROM `user_profile_history`
Expand Down
3 changes: 1 addition & 2 deletions app/repositories/user.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from __future__ import annotations

from collections.abc import Mapping
from typing import Any

from app.common.context import Context
Expand All @@ -18,7 +17,7 @@ async def fetch_one(
self,
user_id: int,
mode: int,
) -> Mapping[str, Any] | None:
) -> dict[str, Any] | None:
query = f"""\
SELECT {self.READ_PARAMS}
FROM `users` `u` INNER JOIN `user_stats` `s` ON `u`.`id` = `s`.`user_id`
Expand Down
27 changes: 13 additions & 14 deletions app/services/database.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from __future__ import annotations

from collections.abc import Mapping
from typing import Any

import databases
Expand All @@ -23,42 +22,42 @@ async def disconnect(self) -> None:
async def fetch_all(
self,
query: ClauseElement | str,
values: dict | None = None,
) -> list[Mapping]:
rows = await self.read_database.fetch_all(query, values) # type: ignore
return [row._mapping for row in rows]
values: dict[str, Any] | None = None,
) -> list[dict[str, Any]]:
rows = await self.read_database.fetch_all(query, values)
return [dict(row._mapping) for row in rows]

async def fetch_one(
self,
query: ClauseElement | str,
values: dict | None = None,
) -> Mapping | None:
row = await self.read_database.fetch_one(query, values) # type: ignore
values: dict[str, Any] | None = None,
) -> dict[str, Any] | None:
row = await self.read_database.fetch_one(query, values)
if row is None:
return None

return row._mapping
return dict(row._mapping)

async def fetch_val(
self,
query: ClauseElement | str,
values: dict | None = None,
values: dict[str, Any] | None = None,
column: Any = 0,
) -> Any:
val = await self.read_database.fetch_val(query, values, column) # type: ignore
val = await self.read_database.fetch_val(query, values, column)
return val

async def execute(
self,
query: ClauseElement | str,
values: dict | None = None,
values: dict[str, Any] | None = None,
) -> Any:
result = await self.write_database.execute(query, values) # type: ignore
result = await self.write_database.execute(query, values)
return result

async def execute_many(
self,
query: ClauseElement | str,
values: list,
values: list[Any],
) -> None:
await self.write_database.execute_many(query, values)
24 changes: 10 additions & 14 deletions app/workers/cronjobs/crawler.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ class PartialUser(TypedDict):
country: str


redis: aioredis.Redis
db: database.Database

redis_map = {
0: ("leaderboard", "std"),
1: ("leaderboard", "taiko"),
Expand All @@ -32,12 +35,9 @@ class PartialUser(TypedDict):
}


async def fetch_rank(
user_id: int,
mode: int,
) -> int:
async def fetch_rank(user_id: int, mode: int) -> int:
(redis_key, mode_name) = redis_map[mode]
current_rank = await redis.zrevrank(f"ripple:{redis_key}:{mode_name}", user_id)
current_rank: int = await redis.zrevrank(f"ripple:{redis_key}:{mode_name}", user_id)

if current_rank is None:
current_rank = 0
Expand All @@ -47,13 +47,9 @@ async def fetch_rank(
return current_rank


async def fetch_c_rank(
user_id: int,
mode: int,
country: str,
) -> int:
async def fetch_c_rank(user_id: int, mode: int, country: str) -> int:
(redis_key, mode_name) = redis_map[mode]
current_rank = await redis.zrevrank(
current_rank: int = await redis.zrevrank(
f"ripple:{redis_key}:{mode_name}:{country.lower()}",
user_id,
)
Expand All @@ -74,7 +70,7 @@ async def fetch_pp(
"user_id": user_id,
"mode": mode,
}
current_pp = await db.fetch_val(
current_pp: int = await db.fetch_val(
"""
SELECT `pp`
FROM `user_stats`
Expand All @@ -97,7 +93,7 @@ async def gather_profile_history(user: PartialUser) -> None:
start_time = int(time.time())

for mode in (0, 1, 2, 3, 4, 5, 6, 8):
latest_pp_awarded = await db.fetch_val(
latest_pp_awarded: int = await db.fetch_val(
"""
SELECT `latest_pp_awarded`
FROM `user_stats`
Expand Down Expand Up @@ -153,7 +149,7 @@ async def gather_profile_history(user: PartialUser) -> None:
async def async_main() -> int:
global redis, db

redis = await aioredis.from_url(
redis = await aioredis.from_url( # type: ignore[no-untyped-call]
f"redis://{settings.REDIS_HOST}:{settings.REDIS_PORT}",
)
db = database.Database(
Expand Down
10 changes: 10 additions & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[mypy]
exclude = (tests|scripts|venv)

strict = True
disallow_untyped_calls = True
enable_error_code = truthy-bool, truthy-iterable, ignore-without-code, unused-awaitable, redundant-expr, possibly-undefined

[mypy-tests.*]
disable_error_code = var-annotated, has-type
allow_untyped_defs = True
6 changes: 6 additions & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
add-trailing-comma
black
mypy
pyupgrade
reorder-python-imports
types-PyYAML

0 comments on commit cd0fb7b

Please sign in to comment.