From e0b413e01666deabe769739fb5db01691fac387d Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Tue, 21 Mar 2023 22:04:32 +0000 Subject: [PATCH] Bump FastAPI to 0.95.0 This release adds support for dependencies and parameters using Annotated and recommends its usage --- api/database.py | 5 +++++ api/routers/codejams.py | 14 ++++++-------- api/routers/infractions.py | 11 +++++------ api/routers/teams.py | 23 ++++++++++++----------- api/routers/users.py | 13 ++++++------- api/routers/winners.py | 12 ++++-------- main-requirements.txt | 12 ++++++------ poetry.lock | 20 ++++++++++---------- pyproject.toml | 2 +- 9 files changed, 55 insertions(+), 57 deletions(-) diff --git a/api/database.py b/api/database.py index 99d9213..14dd35b 100644 --- a/api/database.py +++ b/api/database.py @@ -1,14 +1,19 @@ +from typing import Annotated + +from fastapi import Depends from sqlalchemy import BigInteger, Boolean, Column, Enum, ForeignKey, Index, Integer, PrimaryKeyConstraint, Text, text from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import relationship, sessionmaker from api.constants import Config +from api.dependencies import get_db_session engine = create_async_engine(Config.DATABASE_URL) Base = declarative_base() Session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession) +DBSession = Annotated[AsyncSession, Depends(get_db_session)] class TeamUser(Base): diff --git a/api/routers/codejams.py b/api/routers/codejams.py index 76e1281..156c17e 100644 --- a/api/routers/codejams.py +++ b/api/routers/codejams.py @@ -1,19 +1,17 @@ from typing import Optional -from fastapi import APIRouter, Depends, HTTPException +from fastapi import APIRouter, HTTPException from sqlalchemy import desc, update -from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.future import select -from api.database import Jam, Team, TeamUser, User -from api.dependencies import get_db_session +from api.database import DBSession, Jam, Team, TeamUser, User from api.models import CodeJam, CodeJamResponse router = APIRouter(prefix="/codejams", tags=["codejams"]) @router.get("/") -async def get_codejams(session: AsyncSession = Depends(get_db_session)) -> list[CodeJamResponse]: +async def get_codejams(session: DBSession) -> list[CodeJamResponse]: """Get all the codejams stored in the database.""" codejams = await session.execute(select(Jam).order_by(desc(Jam.id))) codejams.unique() @@ -25,7 +23,7 @@ async def get_codejams(session: AsyncSession = Depends(get_db_session)) -> list[ "/{codejam_id}", responses={404: {"description": "CodeJam could not be found or there is no ongoing code jam."}}, ) -async def get_codejam(codejam_id: int, session: AsyncSession = Depends(get_db_session)) -> CodeJamResponse: +async def get_codejam(codejam_id: int, session: DBSession) -> CodeJamResponse: """ Get a specific codejam stored in the database by ID. @@ -52,9 +50,9 @@ async def get_codejam(codejam_id: int, session: AsyncSession = Depends(get_db_se @router.patch("/{codejam_id}", responses={404: {"description": "Code Jam with specified ID does not exist."}}) async def modify_codejam( codejam_id: int, + session: DBSession, name: Optional[str] = None, ongoing: Optional[bool] = None, - session: AsyncSession = Depends(get_db_session), ) -> CodeJamResponse: """Modify the specified codejam to change its name and/or whether it's the ongoing code jam.""" codejam = await session.execute(select(Jam).where(Jam.id == codejam_id)) @@ -80,7 +78,7 @@ async def modify_codejam( @router.post("/") -async def create_codejam(codejam: CodeJam, session: AsyncSession = Depends(get_db_session)) -> CodeJamResponse: +async def create_codejam(codejam: CodeJam, session: DBSession) -> CodeJamResponse: """ Create a new codejam and get back the one just created. diff --git a/api/routers/infractions.py b/api/routers/infractions.py index 5743ca3..441a138 100644 --- a/api/routers/infractions.py +++ b/api/routers/infractions.py @@ -1,17 +1,16 @@ -from fastapi import APIRouter, Depends, HTTPException -from sqlalchemy.ext.asyncio import AsyncSession +from fastapi import APIRouter, HTTPException from sqlalchemy.future import select +from api.database import DBSession from api.database import Infraction as DbInfraction from api.database import Jam, User -from api.dependencies import get_db_session from api.models import Infraction, InfractionResponse router = APIRouter(prefix="/infractions", tags=["infractions"]) @router.get("/") -async def get_infractions(session: AsyncSession = Depends(get_db_session)) -> list[InfractionResponse]: +async def get_infractions(session: DBSession) -> list[InfractionResponse]: """Get every infraction stored in the database.""" infractions = await session.execute(select(DbInfraction)) infractions.unique() @@ -23,7 +22,7 @@ async def get_infractions(session: AsyncSession = Depends(get_db_session)) -> li "/{infraction_id}", responses={404: {"description": "Infraction could not be found."}}, ) -async def get_infraction(infraction_id: int, session: AsyncSession = Depends(get_db_session)) -> InfractionResponse: +async def get_infraction(infraction_id: int, session: DBSession) -> InfractionResponse: """Get a specific infraction stored in the database by ID.""" infraction_result = await session.execute(select(DbInfraction).where(DbInfraction.id == infraction_id)) infraction_result.unique() @@ -40,7 +39,7 @@ async def get_infraction(infraction_id: int, session: AsyncSession = Depends(get ) async def create_infraction( infraction: Infraction, - session: AsyncSession = Depends(get_db_session), + session: DBSession, ) -> InfractionResponse: """Add an infraction for a user to the database.""" jam_id = (await session.execute(select(Jam.id).where(Jam.id == infraction.jam_id))).scalars().one_or_none() diff --git a/api/routers/teams.py b/api/routers/teams.py index 057ce0e..3292a08 100644 --- a/api/routers/teams.py +++ b/api/routers/teams.py @@ -1,13 +1,12 @@ from typing import Optional -from fastapi import APIRouter, Depends, HTTPException, Response +from fastapi import APIRouter, HTTPException, Response from sqlalchemy import func from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.future import select -from api.database import Jam, Team, TeamUser +from api.database import DBSession, Jam, Team, TeamUser from api.database import User as DbUser -from api.dependencies import get_db_session from api.models import TeamResponse, User router = APIRouter(prefix="/teams", tags=["teams"]) @@ -36,7 +35,7 @@ async def ensure_user_exists(user_id: int, session: AsyncSession) -> DbUser: @router.get("/") -async def get_teams(current_jam: bool = False, session: AsyncSession = Depends(get_db_session)) -> list[TeamResponse]: +async def get_teams(session: DBSession, current_jam: bool = False) -> list[TeamResponse]: """Get every code jam team in the database.""" if not current_jam: teams = await session.execute(select(Team)) @@ -49,7 +48,9 @@ async def get_teams(current_jam: bool = False, session: AsyncSession = Depends(g @router.get("/find", responses={404: {"description": "Team could not be found."}}) async def find_team_by_name( - name: str, jam_id: Optional[int] = None, session: AsyncSession = Depends(get_db_session) + name: str, + session: DBSession, + jam_id: Optional[int] = None, ) -> TeamResponse: """Get a specific code jam team by name.""" if jam_id is None: @@ -70,13 +71,13 @@ async def find_team_by_name( @router.get("/{team_id}", responses={404: {"description": "Team could not be found."}}) -async def get_team(team_id: int, session: AsyncSession = Depends(get_db_session)) -> TeamResponse: +async def get_team(team_id: int, session: DBSession) -> TeamResponse: """Get a specific code jam team in the database by ID.""" return await ensure_team_exists(team_id, session) @router.get("/{team_id}/users", responses={404: {"description": "Team could not be found."}}) -async def get_team_users(team_id: int, session: AsyncSession = Depends(get_db_session)) -> list[User]: +async def get_team_users(team_id: int, session: DBSession) -> list[User]: """Get the users of a specific code jam team in the database.""" await ensure_team_exists(team_id, session) @@ -95,9 +96,7 @@ async def get_team_users(team_id: int, session: AsyncSession = Depends(get_db_se 400: {"description": "This user is already on the team."}, }, ) -async def add_user_to_team( - team_id: int, user_id: int, is_leader: bool = False, session: AsyncSession = Depends(get_db_session) -) -> User: +async def add_user_to_team(team_id: int, user_id: int, session: DBSession, is_leader: bool = False) -> User: """Add a user to a specific code jam team in the database.""" await ensure_team_exists(team_id, session) await ensure_user_exists(user_id, session) @@ -128,7 +127,9 @@ async def add_user_to_team( }, ) async def remove_user_from_team( - team_id: int, user_id: int, session: AsyncSession = Depends(get_db_session) + team_id: int, + user_id: int, + session: DBSession, ) -> Response: """Remove a user from a specific code jam team in the database.""" await ensure_team_exists(team_id, session) diff --git a/api/routers/users.py b/api/routers/users.py index d653f1c..a80c471 100644 --- a/api/routers/users.py +++ b/api/routers/users.py @@ -1,11 +1,10 @@ from typing import Any -from fastapi import APIRouter, Depends, HTTPException +from fastapi import APIRouter, HTTPException from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.future import select -from api.database import Jam, TeamUser, User -from api.dependencies import get_db_session +from api.database import DBSession, Jam, TeamUser, User from api.models import UserResponse, UserTeamResponse router = APIRouter(prefix="/users", tags=["users"]) @@ -48,7 +47,7 @@ async def get_user_data(session: AsyncSession, user_id: int) -> dict[str, Any]: @router.get("/") -async def get_users(session: AsyncSession = Depends(get_db_session)) -> list[UserResponse]: +async def get_users(session: DBSession) -> list[UserResponse]: """Get information about all the users stored in the database.""" users = await session.execute(select(User.id)) users.unique() @@ -57,7 +56,7 @@ async def get_users(session: AsyncSession = Depends(get_db_session)) -> list[Use @router.get("/{user_id}", responses={404: {"description": "User could not be found."}}) -async def get_user(user_id: int, session: AsyncSession = Depends(get_db_session)) -> UserResponse: +async def get_user(user_id: int, session: DBSession) -> UserResponse: """Get a specific user stored in the database by ID.""" user = await session.execute(select(User).where(User.id == user_id)) user.unique() @@ -69,7 +68,7 @@ async def get_user(user_id: int, session: AsyncSession = Depends(get_db_session) @router.post("/{user_id}", responses={400: {"description": "User already exists."}}) -async def create_user(user_id: int, session: AsyncSession = Depends(get_db_session)) -> UserResponse: +async def create_user(user_id: int, session: DBSession) -> UserResponse: """Create a new user with the specified ID to the database.""" user = await session.execute(select(User).where(User.id == user_id)) user.unique() @@ -94,7 +93,7 @@ async def create_user(user_id: int, session: AsyncSession = Depends(get_db_sessi } }, ) -async def get_current_team(user_id: int, session: AsyncSession = Depends(get_db_session)) -> UserTeamResponse: +async def get_current_team(user_id: int, session: DBSession) -> UserTeamResponse: """Get a user's current team information.""" user = await session.execute(select(User).where(User.id == user_id)) user.unique() diff --git a/api/routers/winners.py b/api/routers/winners.py index 974c506..e5eff2a 100644 --- a/api/routers/winners.py +++ b/api/routers/winners.py @@ -1,11 +1,9 @@ -from fastapi import APIRouter, Depends, HTTPException +from fastapi import APIRouter, HTTPException from sqlalchemy import func -from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.future import select -from api.database import Jam, User +from api.database import DBSession, Jam, User from api.database import Winner as DbWinner -from api.dependencies import get_db_session from api.models import Winner, WinnerResponse router = APIRouter(prefix="/winners", tags=["winners"]) @@ -15,7 +13,7 @@ "/{jam_id}", responses={404: {"description": "The specified codejam could not be found."}}, ) -async def get_winners(jam_id: int, session: AsyncSession = Depends(get_db_session)) -> list[WinnerResponse]: +async def get_winners(jam_id: int, session: DBSession) -> list[WinnerResponse]: """Get the top ten winners from the specified codejam.""" jam = await session.execute(select(Jam).where(Jam.id == jam_id)) jam.unique() @@ -38,9 +36,7 @@ async def get_winners(jam_id: int, session: AsyncSession = Depends(get_db_sessio 409: {"description": "One or more users are already a winner in the specified codejam."}, }, ) -async def create_winners( - jam_id: int, winners: list[Winner], session: AsyncSession = Depends(get_db_session) -) -> list[WinnerResponse]: +async def create_winners(jam_id: int, winners: list[Winner], session: DBSession) -> list[WinnerResponse]: """Add the top ten winners to the specified codejam.""" jam = await session.execute(select(Jam).where(Jam.id == jam_id)) jam.unique() diff --git a/main-requirements.txt b/main-requirements.txt index a8fa0bc..0e604b5 100644 --- a/main-requirements.txt +++ b/main-requirements.txt @@ -47,9 +47,9 @@ click==8.1.3 ; python_full_version >= "3.11.0" and python_full_version < "3.12.0 colorama==0.4.6 ; python_full_version >= "3.11.0" and python_full_version < "3.12.0" and sys_platform == "win32" or python_full_version >= "3.11.0" and python_full_version < "3.12.0" and platform_system == "Windows" \ --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 -fastapi==0.89.0 ; python_full_version >= "3.11.0" and python_full_version < "3.12.0" \ - --hash=sha256:34990132751324c988db7bd15bf43a86f5c8580e58a4438e14b12d72fb0ae1d1 \ - --hash=sha256:d8cafbd223b5a3c010119ba61fb246d5fd3b8f4766cfa93f922a416650797976 +fastapi==0.95.0 ; python_full_version >= "3.11.0" and python_full_version < "3.12.0" \ + --hash=sha256:99d4fdb10e9dd9a24027ac1d0bd4b56702652056ca17a6c8721eec4ad2f14e18 \ + --hash=sha256:daf73bbe844180200be7966f68e8ec9fd8be57079dff1bacb366db32729e6eb5 greenlet==2.0.2 ; python_full_version >= "3.11.0" and platform_machine == "aarch64" and python_full_version < "3.12.0" or python_full_version >= "3.11.0" and platform_machine == "ppc64le" and python_full_version < "3.12.0" or python_full_version >= "3.11.0" and platform_machine == "x86_64" and python_full_version < "3.12.0" or python_full_version >= "3.11.0" and platform_machine == "amd64" and python_full_version < "3.12.0" or python_full_version >= "3.11.0" and platform_machine == "AMD64" and python_full_version < "3.12.0" or python_full_version >= "3.11.0" and platform_machine == "win32" and python_full_version < "3.12.0" or python_full_version >= "3.11.0" and platform_machine == "WIN32" and python_full_version < "3.12.0" \ --hash=sha256:03a8f4f3430c3b3ff8d10a2a86028c660355ab637cee9333d63d66b56f09d52a \ --hash=sha256:0bf60faf0bc2468089bdc5edd10555bab6e85152191df713e2ab1fcc86382b5a \ @@ -390,9 +390,9 @@ sqlalchemy[asyncio,postgresql-asyncpg]==1.4.45 ; python_full_version >= "3.11.0" --hash=sha256:f61e54b8c2b389de1a8ad52394729c478c67712dbdcdadb52c2575e41dae94a5 \ --hash=sha256:f7944b04e6fcf8d733964dd9ee36b6a587251a1a4049af3a9b846f6e64eb349a \ --hash=sha256:fd69850860093a3f69fefe0ab56d041edfdfe18510b53d9a2eaecba2f15fa795 -starlette==0.22.0 ; python_full_version >= "3.11.0" and python_full_version < "3.12.0" \ - --hash=sha256:b092cbc365bea34dd6840b42861bdabb2f507f8671e642e8272d2442e08ea4ff \ - --hash=sha256:b5eda991ad5f0ee5d8ce4c4540202a573bb6691ecd0c712262d0bc85cf8f2c50 +starlette==0.26.1 ; python_full_version >= "3.11.0" and python_full_version < "3.12.0" \ + --hash=sha256:41da799057ea8620e4667a3e69a5b1923ebd32b1819c8fa75634bbe8d8bea9bd \ + --hash=sha256:e87fce5d7cbdde34b76f0ac69013fd9d190d581d80681493016666e6f96c6d5e typing-extensions==4.5.0 ; python_full_version >= "3.11.0" and python_full_version < "3.12.0" \ --hash=sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb \ --hash=sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4 diff --git a/poetry.lock b/poetry.lock index f6c6c2f..ec7154d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -674,25 +674,25 @@ pgp = ["gpg"] [[package]] name = "fastapi" -version = "0.89.0" +version = "0.95.0" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "fastapi-0.89.0-py3-none-any.whl", hash = "sha256:34990132751324c988db7bd15bf43a86f5c8580e58a4438e14b12d72fb0ae1d1"}, - {file = "fastapi-0.89.0.tar.gz", hash = "sha256:d8cafbd223b5a3c010119ba61fb246d5fd3b8f4766cfa93f922a416650797976"}, + {file = "fastapi-0.95.0-py3-none-any.whl", hash = "sha256:daf73bbe844180200be7966f68e8ec9fd8be57079dff1bacb366db32729e6eb5"}, + {file = "fastapi-0.95.0.tar.gz", hash = "sha256:99d4fdb10e9dd9a24027ac1d0bd4b56702652056ca17a6c8721eec4ad2f14e18"}, ] [package.dependencies] pydantic = ">=1.6.2,<1.7 || >1.7,<1.7.1 || >1.7.1,<1.7.2 || >1.7.2,<1.7.3 || >1.7.3,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0" -starlette = "0.22.0" +starlette = ">=0.26.1,<0.27.0" [package.extras] all = ["email-validator (>=1.1.1)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "python-multipart (>=0.0.5)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] dev = ["pre-commit (>=2.17.0,<3.0.0)", "ruff (==0.0.138)", "uvicorn[standard] (>=0.12.0,<0.21.0)"] -doc = ["mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pyyaml (>=5.3.1,<7.0.0)", "typer[all] (>=0.6.1,<0.8.0)"] -test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==22.10.0)", "coverage[toml] (>=6.5.0,<8.0)", "databases[sqlite] (>=0.3.2,<0.7.0)", "email-validator (>=1.1.1,<2.0.0)", "flask (>=1.1.2,<3.0.0)", "httpx (>=0.23.0,<0.24.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.982)", "orjson (>=3.2.1,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "peewee (>=3.13.3,<4.0.0)", "pytest (>=7.1.3,<8.0.0)", "python-jose[cryptography] (>=3.3.0,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "pyyaml (>=5.3.1,<7.0.0)", "ruff (==0.0.138)", "sqlalchemy (>=1.3.18,<1.4.43)", "types-orjson (==3.6.2)", "types-ujson (==5.6.0.0)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)"] +doc = ["mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pyyaml (>=5.3.1,<7.0.0)", "typer-cli (>=0.0.13,<0.0.14)", "typer[all] (>=0.6.1,<0.8.0)"] +test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==23.1.0)", "coverage[toml] (>=6.5.0,<8.0)", "databases[sqlite] (>=0.3.2,<0.7.0)", "email-validator (>=1.1.1,<2.0.0)", "flask (>=1.1.2,<3.0.0)", "httpx (>=0.23.0,<0.24.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.982)", "orjson (>=3.2.1,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "peewee (>=3.13.3,<4.0.0)", "pytest (>=7.1.3,<8.0.0)", "python-jose[cryptography] (>=3.3.0,<4.0.0)", "python-multipart (>=0.0.5,<0.0.7)", "pyyaml (>=5.3.1,<7.0.0)", "ruff (==0.0.138)", "sqlalchemy (>=1.3.18,<1.4.43)", "types-orjson (==3.6.2)", "types-ujson (==5.7.0.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)"] [[package]] name = "filelock" @@ -2352,14 +2352,14 @@ sqlcipher = ["sqlcipher3-binary"] [[package]] name = "starlette" -version = "0.22.0" +version = "0.26.1" description = "The little ASGI library that shines." category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "starlette-0.22.0-py3-none-any.whl", hash = "sha256:b5eda991ad5f0ee5d8ce4c4540202a573bb6691ecd0c712262d0bc85cf8f2c50"}, - {file = "starlette-0.22.0.tar.gz", hash = "sha256:b092cbc365bea34dd6840b42861bdabb2f507f8671e642e8272d2442e08ea4ff"}, + {file = "starlette-0.26.1-py3-none-any.whl", hash = "sha256:e87fce5d7cbdde34b76f0ac69013fd9d190d581d80681493016666e6f96c6d5e"}, + {file = "starlette-0.26.1.tar.gz", hash = "sha256:41da799057ea8620e4667a3e69a5b1923ebd32b1819c8fa75634bbe8d8bea9bd"}, ] [package.dependencies] @@ -2769,4 +2769,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more [metadata] lock-version = "2.0" python-versions = "3.11.*" -content-hash = "6999c9f76e9622f528f9fbecc19ccf5a6bade1634e3d752eb7aa8577dbf4b209" +content-hash = "faa209d01da0615cde05d1aa51595524870f7e388e6253591fb0ddcbdd17dcc2" diff --git a/pyproject.toml b/pyproject.toml index 8932236..5192602 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ license = "MIT" python = "3.11.*" alembic = {version = "1.10.2", extras = ["tz"]} -fastapi = "0.89.0" +fastapi = "0.95.0" python-decouple = "3.8" SQLAlchemy = {version = "1.4.45", extras = ["asyncio", "postgresql_asyncpg"]} uvicorn = {version = "0.21.1", extras = ["standard"]}