diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 58de18a..b1ec64c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -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' diff --git a/.ruff.toml b/.ruff.toml index b47ea80..15e3dbc 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -9,11 +9,14 @@ select = [ "PGH004", "PLE1142", "RUF100", + "I002", + "F404", + "TCH", + "UP007" ] -ignore = ["F401"] line-length = 120 format = "grouped" -target-version = "py38" +target-version = "py310" cache-dir = "./.ruff_cache" [flake8-pytest-style] @@ -34,3 +37,8 @@ ignore-variadic-names = true [isort] lines-between-types = 1 order-by-type = true + +[per-file-ignores] +"backend/app/api/v1/*.py" = ["TCH"] +"backend/app/models/*.py" = ["TCH003"] +"backend/app/**/__init__.py" = ["F401"] diff --git a/README.md b/README.md index 5024f10..e33651a 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ This is a base project of the FastAPI framework. It‘s purpose is to allow you to develop your project directly with it as your base project +Support python3.10 and above + ## Skill - [x] FastAPI diff --git a/backend/app/api/jwt.py b/backend/app/api/jwt.py index 9033091..9b4b7f3 100644 --- a/backend/app/api/jwt.py +++ b/backend/app/api/jwt.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- from datetime import datetime, timedelta -from typing import Any, Union +from typing import Any from fastapi import Depends from fastapi.security import OAuth2PasswordBearer @@ -42,7 +42,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 diff --git a/backend/app/common/pagination.py b/backend/app/common/pagination.py index e195d57..9085ffd 100644 --- a/backend/app/common/pagination.py +++ b/backend/app/common/pagination.py @@ -3,7 +3,7 @@ from __future__ import annotations import math -from typing import TypeVar, Generic, Sequence, Dict, Union +from typing import TypeVar, Generic, Sequence, Dict from fastapi import Query from fastapi_pagination.bases import AbstractPage, AbstractParams, RawParams @@ -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 diff --git a/backend/app/common/response/response_schema.py b/backend/app/common/response/response_schema.py index e2909d8..87d3476 100644 --- a/backend/app/common/response/response_schema.py +++ b/backend/app/common/response/response_schema.py @@ -1,12 +1,12 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- from datetime import datetime -from typing import Optional, Any, Union, Set, Dict +from typing import Any, Union, Set, Dict from fastapi.encoders import jsonable_encoder from pydantic import validate_arguments, BaseModel -_JsonEncoder = Union[Set[Union[int, str]], Dict[Union[int, str], Any]] +_JsonEncoder = Union[Set[int | str], Dict[int | str, Any]] __all__ = ['ResponseModel', 'response_base'] @@ -18,7 +18,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')} @@ -31,9 +31,7 @@ class ResponseBase: @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): """ 请求成功返回通用方法 @@ -48,13 +46,13 @@ class ResponseBase: @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}) diff --git a/backend/app/core/conf.py b/backend/app/core/conf.py index 59f5363..6675ae8 100644 --- a/backend/app/core/conf.py +++ b/backend/app/core/conf.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- from functools import lru_cache -from typing import Optional from pydantic import BaseSettings, root_validator @@ -35,9 +34,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): diff --git a/backend/app/crud/base.py b/backend/app/crud/base.py index c22a51e..d7c9cbf 100644 --- a/backend/app/crud/base.py +++ b/backend/app/crud/base.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -from typing import Any, Dict, Generic, Type, TypeVar, Union, Optional, NoReturn +from typing import Any, Dict, Generic, Type, TypeVar, NoReturn from pydantic import BaseModel from sqlalchemy import select, update, delete @@ -17,7 +17,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 获取一条数据 @@ -28,7 +28,7 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]): 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: """ 新增一条数据 @@ -44,7 +44,7 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]): 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 更新一条数据 diff --git a/backend/app/crud/crud_user.py b/backend/app/crud/crud_user.py index 7138fab..610b213 100644 --- a/backend/app/crud/crud_user.py +++ b/backend/app/crud/crud_user.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -from typing import Optional, NoReturn +from typing import NoReturn from sqlalchemy import func, select, update, desc from sqlalchemy.ext.asyncio import AsyncSession @@ -13,10 +13,10 @@ from backend.app.schemas.user import CreateUser, UpdateUser, Avatar 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() diff --git a/backend/app/database/base_class.py b/backend/app/database/base_class.py index 280c794..0bb7637 100644 --- a/backend/app/database/base_class.py +++ b/backend/app/database/base_class.py @@ -2,7 +2,6 @@ # -*- coding: utf-8 -*- import uuid from datetime import datetime -from typing import Optional from sqlalchemy import func from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, declared_attr, MappedAsDataclass @@ -24,9 +23,9 @@ class _BaseMixin(MappedAsDataclass): """ create_user: Mapped[int] = mapped_column(sort_order=9999, comment='创建者') - update_user: Mapped[Optional[int]] = mapped_column(init=False, default=None, sort_order=9999, comment='修改者') + update_user: Mapped[int | None] = mapped_column(init=False, default=None, sort_order=9999, comment='修改者') created_time: Mapped[datetime] = mapped_column(init=False, default=func.now(), sort_order=9999, comment='创建时间') - updated_time: Mapped[Optional[datetime]] = mapped_column( + updated_time: Mapped[datetime | None] = mapped_column( init=False, onupdate=func.now(), sort_order=9999, comment='更新时间' ) diff --git a/backend/app/models/user.py b/backend/app/models/user.py index f2ceca7..4a9123c 100644 --- a/backend/app/models/user.py +++ b/backend/app/models/user.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- from datetime import datetime -from typing import Optional from sqlalchemy import func, String from sqlalchemy.orm import Mapped, mapped_column @@ -21,7 +20,7 @@ class User(DataClassBase): email: Mapped[str] = mapped_column(String(50), unique=True, index=True, comment='邮箱') is_superuser: Mapped[bool] = mapped_column(default=False, comment='超级权限') is_active: Mapped[bool] = mapped_column(default=True, comment='用户账号状态') - avatar: Mapped[Optional[str]] = mapped_column(String(255), default=None, comment='头像') - mobile_number: Mapped[Optional[str]] = mapped_column(String(11), default=None, comment='手机号') + avatar: Mapped[str | None] = mapped_column(String(255), default=None, comment='头像') + mobile_number: Mapped[str | None] = mapped_column(String(11), default=None, comment='手机号') time_joined: Mapped[datetime] = mapped_column(init=False, default=func.now(), comment='注册时间') - last_login: Mapped[Optional[datetime]] = mapped_column(init=False, onupdate=func.now(), comment='上次登录') + last_login: Mapped[datetime | None] = mapped_column(init=False, onupdate=func.now(), comment='上次登录') diff --git a/backend/app/schemas/token.py b/backend/app/schemas/token.py index 088e432..45a1b70 100644 --- a/backend/app/schemas/token.py +++ b/backend/app/schemas/token.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -from typing import Optional from pydantic import BaseModel @@ -10,4 +9,4 @@ class Token(BaseModel): msg: str = 'Success' access_token: str token_type: str = 'Bearer' - is_superuser: Optional[bool] = None + is_superuser: bool | None = None diff --git a/backend/app/schemas/user.py b/backend/app/schemas/user.py index 18503bc..75f5ae7 100644 --- a/backend/app/schemas/user.py +++ b/backend/app/schemas/user.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- import datetime -from typing import Optional from pydantic import BaseModel, Field, HttpUrl @@ -18,7 +17,7 @@ class CreateUser(Auth): class UpdateUser(BaseModel): username: str email: str - mobile_number: Optional[str] = None + mobile_number: str | None = None class Avatar(BaseModel): @@ -28,9 +27,9 @@ class Avatar(BaseModel): class GetUserInfo(UpdateUser): id: int uid: str - avatar: Optional[str] = None + avatar: str | None = None time_joined: datetime.datetime = None - last_login: Optional[datetime.datetime] = None + last_login: datetime.datetime | None = None is_superuser: bool is_active: bool diff --git a/docker_conf.py b/docker_conf.py index d5ae4d6..327f105 100644 --- a/docker_conf.py +++ b/docker_conf.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- from functools import lru_cache -from typing import Optional from pydantic import BaseSettings @@ -11,9 +10,9 @@ class Settings(BaseSettings): TITLE: str = 'FastAPI' VERSION: str = 'v0.0.1' DESCRIPTION: str = 'FastAPI Best Architecture' - DOCS_URL: Optional[str] = '/v1/docs' - REDOCS_URL: Optional[str] = None - OPENAPI_URL: Optional[str] = '/v1/openapi' + DOCS_URL: str | None = '/v1/docs' + REDOCS_URL: str | None = None + OPENAPI_URL: str | None = '/v1/openapi' # Static Server STATIC_FILES: bool = False