Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ repos:
rev: 23.3.0
hooks:
- id: black
language_version: python3.8
language_version: python3.10
args:
- '--skip-string-normalization'
- '--line-length'
- '120'
- '--target-version'
- 'py38'
- 'py310'
9 changes: 9 additions & 0 deletions .ruff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ select = [
"PGH004",
"PLE1142",
"RUF100",
"I002",
"F404",
"TCH",
"UP007"
]
ignore = ["F401"]
line-length = 120
Expand All @@ -34,3 +38,8 @@ ignore-variadic-names = true
[isort]
lines-between-types = 1
order-by-type = true
required-imports = ["from __future__ import annotations"]

[per-file-ignores]
"backend/app/api/v1/*.py" = ["TCH"]
"backend/app/models/*.py" = ["TCH003"]
1 change: 1 addition & 0 deletions backend/app/alembic/env.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
from __future__ import annotations
import asyncio
import os
import sys
Expand Down
8 changes: 5 additions & 3 deletions backend/app/api/jwt.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import annotations

from datetime import datetime, timedelta
from typing import Any, Union
from typing import Any

from fastapi import Depends
from fastapi.security import OAuth2PasswordBearer
Expand All @@ -13,7 +15,7 @@
from backend.app.common.exception.errors import AuthorizationError, TokenError
from backend.app.core.conf import settings
from backend.app.crud.crud_user import UserDao
from backend.app.database.db_mysql import CurrentSession
from backend.app.database.db_mysql import CurrentSession # noqa: TCH001
from backend.app.models import User

pwd_context = CryptContext(schemes=['bcrypt'], deprecated='auto')
Expand Down Expand Up @@ -42,7 +44,7 @@ def password_verify(plain_password: str, hashed_password: str) -> bool:
return pwd_context.verify(plain_password, hashed_password)


def create_access_token(data: Union[int, Any], expires_delta: Union[timedelta, None] = None) -> str:
def create_access_token(data: int | Any, expires_delta: timedelta | None = None) -> str:
"""
Generate encryption token

Expand Down
1 change: 1 addition & 0 deletions backend/app/api/routers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import annotations
from fastapi import APIRouter

from backend.app.api.v1.auth import router as auth_router
Expand Down
12 changes: 9 additions & 3 deletions backend/app/api/service/user_service.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import annotations

from typing import TYPE_CHECKING

from email_validator import validate_email, EmailNotValidError
from fastapi.security import OAuth2PasswordRequestForm
from fastapi_pagination.ext.sqlalchemy import paginate

from backend.app.api import jwt
from backend.app.common.exception import errors
from backend.app.crud.crud_user import UserDao
from backend.app.database.db_mysql import async_db_session
from backend.app.models import User
from backend.app.schemas.user import CreateUser, ResetPassword, UpdateUser, Avatar
from backend.app.utils import re_verify

if TYPE_CHECKING:
from fastapi.security import OAuth2PasswordRequestForm
from backend.app.models import User
from backend.app.schemas.user import CreateUser, ResetPassword, UpdateUser, Avatar


class UserService:
@staticmethod
Expand Down
1 change: 1 addition & 0 deletions backend/app/api/v1/auth/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import annotations
from backend.app.api.v1.auth.user import router
2 changes: 2 additions & 0 deletions backend/app/api/v1/auth/user.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import annotations

from fastapi import APIRouter, Depends
from fastapi.security import OAuth2PasswordRequestForm

Expand Down
4 changes: 3 additions & 1 deletion backend/app/api/v1/task_demo.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import annotations

import datetime
from typing import Annotated

from fastapi import APIRouter, Query
from typing_extensions import Annotated

from backend.app.common.task import scheduler

Expand Down
1 change: 1 addition & 0 deletions backend/app/common/enums.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import annotations
from enum import Enum


Expand Down
6 changes: 4 additions & 2 deletions backend/app/common/exception/errors.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from typing import Any
from __future__ import annotations
from typing import Any, TYPE_CHECKING

from fastapi import HTTPException

from backend.app.common.response.response_code import CodeEnum
if TYPE_CHECKING:
from backend.app.common.response.response_code import CodeEnum


class BaseExceptionMixin(Exception):
Expand Down
6 changes: 5 additions & 1 deletion backend/app/common/exception/exception_handler.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import annotations
import json
from typing import TYPE_CHECKING

from fastapi import FastAPI, Request
from fastapi.exceptions import RequestValidationError, HTTPException
from pydantic import ValidationError
from starlette.responses import JSONResponse
Expand All @@ -12,6 +13,9 @@
from backend.app.common.response.response_schema import response_base
from backend.app.core.conf import settings

if TYPE_CHECKING:
from fastapi import FastAPI, Request


def _get_exception_code(status_code):
"""
Expand Down
5 changes: 4 additions & 1 deletion backend/app/common/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
from __future__ import annotations

import os
from typing import TYPE_CHECKING

import loguru
from loguru import logger

from backend.app.core import path_conf
from backend.app.core.conf import settings

if TYPE_CHECKING:
import loguru


class Logger:
@staticmethod
Expand Down
2 changes: 1 addition & 1 deletion backend/app/common/pagination.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class Page(AbstractPage[T], Generic[T]):
page: int # 第n页
size: int # 每页数量
total_pages: int # 总页数
links: Dict[str, Union[str, None]] # 跳转链接
links: Dict[str, str | None] # 跳转链接

__params_type__ = Params # 使用自定义的Params

Expand Down
1 change: 1 addition & 0 deletions backend/app/common/redis.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import annotations
import sys

from aioredis import Redis, TimeoutError, AuthenticationError
Expand Down
1 change: 1 addition & 0 deletions backend/app/common/response/response_code.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import annotations
from enum import Enum


Expand Down
11 changes: 5 additions & 6 deletions backend/app/common/response/response_schema.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import annotations
from datetime import datetime
from typing import Optional, Any, Union, Set, Dict

Expand All @@ -18,7 +19,7 @@ class ResponseModel(BaseModel):

code: int = 200
msg: str = 'Success'
data: Optional[Any] = None
data: Any | None = None

class Config:
json_encoders = {datetime: lambda x: x.strftime('%Y-%m-%d %H:%M:%S')}
Expand All @@ -31,9 +32,7 @@ def __encode_json(data: Any):

@staticmethod
@validate_arguments
def success(
*, code: int = 200, msg: str = 'Success', data: Optional[Any] = None, exclude: Optional[_JsonEncoder] = None
):
def success(*, code: int = 200, msg: str = 'Success', data: Any | None = None, exclude: _JsonEncoder | None = None):
"""
请求成功返回通用方法

Expand All @@ -48,13 +47,13 @@ def success(

@staticmethod
@validate_arguments
def fail(*, code: int = 400, msg: str = 'Bad Request', data: Any = None, exclude: Optional[_JsonEncoder] = None):
def fail(*, code: int = 400, msg: str = 'Bad Request', data: Any = None, exclude: _JsonEncoder | None = None):
data = data if data is None else ResponseBase.__encode_json(data)
return ResponseModel(code=code, msg=msg, data=data).dict(exclude={'data': exclude})

@staticmethod
@validate_arguments
def response_200(*, msg: str = 'Success', data: Optional[Any] = None, exclude: Optional[_JsonEncoder] = None):
def response_200(*, msg: str = 'Success', data: Any | None = None, exclude: _JsonEncoder | None = None):
data = data if data is None else ResponseBase.__encode_json(data)
return ResponseModel(code=200, msg=msg, data=data).dict(exclude={'data': exclude})

Expand Down
1 change: 1 addition & 0 deletions backend/app/common/task.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# !/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import annotations
import tzlocal
from apscheduler.executors.asyncio import AsyncIOExecutor
from apscheduler.jobstores.redis import RedisJobStore
Expand Down
9 changes: 5 additions & 4 deletions backend/app/core/conf.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import annotations

from functools import lru_cache
from typing import Optional

from pydantic import BaseSettings, root_validator

Expand Down Expand Up @@ -35,9 +36,9 @@ class Settings(BaseSettings):
TITLE: str = 'FastAPI'
VERSION: str = '0.0.1'
DESCRIPTION: str = 'FastAPI Best Architecture'
DOCS_URL: Optional[str] = '/v1/docs'
REDOCS_URL: Optional[str] = '/v1/redocs'
OPENAPI_URL: Optional[str] = '/v1/openapi'
DOCS_URL: str | None = '/v1/docs'
REDOCS_URL: str | None = '/v1/redocs'
OPENAPI_URL: str | None = '/v1/openapi'

@root_validator
def validator_api_url(cls, values):
Expand Down
1 change: 1 addition & 0 deletions backend/app/core/path_conf.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import annotations
import os
from pathlib import Path

Expand Down
1 change: 1 addition & 0 deletions backend/app/core/registrar.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import annotations
from contextlib import asynccontextmanager

from fastapi import FastAPI
Expand Down
14 changes: 9 additions & 5 deletions backend/app/crud/base.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from typing import Any, Dict, Generic, Type, TypeVar, Union, Optional, NoReturn
from __future__ import annotations

from typing import Any, Dict, Generic, Type, TypeVar, NoReturn, TYPE_CHECKING

from pydantic import BaseModel
from sqlalchemy import select, update, delete
from sqlalchemy.ext.asyncio import AsyncSession

from backend.app.database.base_class import MappedBase

if TYPE_CHECKING:
from sqlalchemy.ext.asyncio import AsyncSession

ModelType = TypeVar('ModelType', bound=MappedBase)
CreateSchemaType = TypeVar('CreateSchemaType', bound=BaseModel)
UpdateSchemaType = TypeVar('UpdateSchemaType', bound=BaseModel)
Expand All @@ -17,7 +21,7 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]):
def __init__(self, model: Type[ModelType]):
self.model = model

async def get(self, db: AsyncSession, pk: int) -> Optional[ModelType]:
async def get(self, db: AsyncSession, pk: int) -> ModelType | None:
"""
通过主键 id 获取一条数据

Expand All @@ -28,7 +32,7 @@ async def get(self, db: AsyncSession, pk: int) -> Optional[ModelType]:
model = await db.execute(select(self.model).where(self.model.id == pk))
return model.scalars().first()

async def create(self, db: AsyncSession, obj_in: CreateSchemaType, user_id: Optional[int] = None) -> NoReturn:
async def create(self, db: AsyncSession, obj_in: CreateSchemaType, user_id: int | None = None) -> NoReturn:
"""
新增一条数据

Expand All @@ -44,7 +48,7 @@ async def create(self, db: AsyncSession, obj_in: CreateSchemaType, user_id: Opti
db.add(db_obj)

async def update(
self, db: AsyncSession, pk: int, obj_in: Union[UpdateSchemaType, Dict[str, Any]], user_id: Optional[int] = None
self, db: AsyncSession, pk: int, obj_in: UpdateSchemaType | Dict[str, Any], user_id: int | None = None
) -> int:
"""
通过主键 id 更新一条数据
Expand Down
13 changes: 8 additions & 5 deletions backend/app/crud/crud_user.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from typing import Optional, NoReturn
from __future__ import annotations
from typing import Optional, NoReturn, TYPE_CHECKING

from sqlalchemy import func, select, update, desc
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.sql import Select

from backend.app.api import jwt
from backend.app.crud.base import CRUDBase
from backend.app.models import User
from backend.app.schemas.user import CreateUser, UpdateUser, Avatar

if TYPE_CHECKING:
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.sql import Select


class CRUDUser(CRUDBase[User, CreateUser, UpdateUser]):
async def get_user_by_id(self, db: AsyncSession, user_id: int) -> Optional[User]:
async def get_user_by_id(self, db: AsyncSession, user_id: int) -> User | None:
return await self.get(db, user_id)

async def get_user_by_username(self, db: AsyncSession, username: str) -> Optional[User]:
async def get_user_by_username(self, db: AsyncSession, username: str) -> User | None:
user = await db.execute(select(self.model).where(self.model.username == username))
return user.scalars().first()

Expand Down
Loading