refactor(backend): migrate type checking from mypy to pyright

Replace mypy>=1.8.0 with pyright>=1.1.390. Remove all [tool.mypy] and
[tool.pydantic-mypy] sections from pyproject.toml and add
pyrightconfig.json (standard mode, SQLAlchemy false-positive rules
suppressed globally).

Fixes surfaced by pyright:
- Remove unreachable except AuthError clauses in login/login_oauth (same class as AuthenticationError)
- Fix Pydantic v2 list Field: min_items/max_items → min_length/max_length
- Split OAuthProviderConfig TypedDict into required + optional(email_url) inheritance
- Move JWTError/ExpiredSignatureError from lazy try-block imports to module level
- Add timezone-aware guard to UserSession.is_expired to match sibling models
- Fix is_active: bool → bool | None in three organization repo signatures
- Initialize search_filter = None before conditional block (possibly unbound fix)
- Add bool() casts to model is_expired and repo is_active/is_superuser returns
- Restructure except (JWTError, Exception) into separate except clauses
This commit is contained in:
2026-02-28 19:12:40 +01:00
parent 4c6bf55bcc
commit a8aa416ecb
17 changed files with 85 additions and 201 deletions

View File

@@ -174,6 +174,7 @@ class OrganizationRepository(
if is_active is not None:
query = query.where(Organization.is_active == is_active)
search_filter = None
if search:
search_filter = or_(
Organization.name.ilike(f"%{search}%"),
@@ -185,7 +186,7 @@ class OrganizationRepository(
count_query = select(func.count(Organization.id))
if is_active is not None:
count_query = count_query.where(Organization.is_active == is_active)
if search:
if search_filter is not None:
count_query = count_query.where(search_filter)
count_result = await db.execute(count_query)
@@ -333,7 +334,7 @@ class OrganizationRepository(
organization_id: UUID,
skip: int = 0,
limit: int = 100,
is_active: bool = True,
is_active: bool | None = True,
) -> tuple[list[dict[str, Any]], int]:
"""Get members of an organization with user details."""
try:
@@ -387,7 +388,7 @@ class OrganizationRepository(
raise
async def get_user_organizations(
self, db: AsyncSession, *, user_id: UUID, is_active: bool = True
self, db: AsyncSession, *, user_id: UUID, is_active: bool | None = True
) -> list[Organization]:
"""Get all organizations a user belongs to."""
try:
@@ -410,7 +411,7 @@ class OrganizationRepository(
raise
async def get_user_organizations_with_details(
self, db: AsyncSession, *, user_id: UUID, is_active: bool = True
self, db: AsyncSession, *, user_id: UUID, is_active: bool | None = True
) -> list[dict[str, Any]]:
"""Get user's organizations with role and member count in SINGLE QUERY."""
try:
@@ -476,7 +477,7 @@ class OrganizationRepository(
)
user_org = result.scalar_one_or_none()
return user_org.role if user_org else None
return user_org.role if user_org else None # pyright: ignore[reportReturnType]
except Exception as e:
logger.error(f"Error getting user role in org: {e!s}")
raise

View File

@@ -256,11 +256,11 @@ class UserRepository(BaseRepository[User, UserCreate, UserUpdate]):
def is_active(self, user: User) -> bool:
"""Check if user is active."""
return user.is_active
return bool(user.is_active)
def is_superuser(self, user: User) -> bool:
"""Check if user is a superuser."""
return user.is_superuser
return bool(user.is_superuser)
# Singleton instance