Skip to content

Commit

Permalink
Updated codebase for FastAPI 0.101.0 and Pydantic 2 (#51)
Browse files Browse the repository at this point in the history
  • Loading branch information
TeKrop authored Aug 6, 2023
1 parent d9a0937 commit 39c5466
Show file tree
Hide file tree
Showing 30 changed files with 341 additions and 260 deletions.
3 changes: 2 additions & 1 deletion app/common/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import logging
import sys
from pathlib import Path
from typing import ClassVar

from loguru import logger as loguru_logger

Expand All @@ -14,7 +15,7 @@ class InterceptHandler(logging.Handler):
to transform them into loguru logs.
"""

loglevel_mapping = {
loglevel_mapping: ClassVar[dict] = {
50: "CRITICAL",
40: "ERROR",
30: "WARNING",
Expand Down
3 changes: 2 additions & 1 deletion app/common/metaclasses.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
"""Set of metaclasses for the project"""
from typing import ClassVar


class Singleton(type):
"""Singleton class, to be used as metaclass."""

_instances = {}
_instances: ClassVar[dict] = {}

def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
Expand Down
13 changes: 9 additions & 4 deletions app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from functools import cache
from pathlib import Path

from pydantic import BaseSettings
from pydantic_settings import BaseSettings, SettingsConfigDict


@cache
Expand All @@ -14,10 +14,18 @@ def get_app_version() -> str:


class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env")

############
# APPLICATION SETTINGS
############

# Application volume path for container
app_volume_path: str = "/my/custom/path/for/app"

# Application port
app_port: int = 80

# Application version, retrieved from pyproject.toml. It should never be
# overriden in dotenv. Only used in OpenAPI spec and request headers.
app_version: str = get_app_version()
Expand Down Expand Up @@ -154,9 +162,6 @@ class Settings(BaseSettings):
# Root path for Loguru access logs. It should never be overriden in dotenv.
logs_root_path: str = f"{Path.cwd()}/logs"

class Config:
env_file = ".env"


@cache
def get_settings() -> Settings:
Expand Down
30 changes: 16 additions & 14 deletions app/handlers/get_hero_request_handler.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
"""Hero Request Handler module"""
from typing import ClassVar

from app.common.helpers import dict_insert_value_before_key
from app.config import settings
from app.parsers.hero_parser import HeroParser
Expand All @@ -14,7 +16,7 @@ class GetHeroRequestHandler(APIRequestHandler):
should be used to display data about a specific hero.
"""

parser_classes = [HeroParser, HeroesParser, HeroesStatsParser]
parser_classes: ClassVar[list] = [HeroParser, HeroesParser, HeroesStatsParser]
timeout = settings.hero_path_cache_timeout

def merge_parsers_data(self, parsers_data: list[dict], **kwargs) -> dict:
Expand All @@ -26,32 +28,32 @@ def merge_parsers_data(self, parsers_data: list[dict], **kwargs) -> dict:
hero_data, heroes_data, heroes_stats_data = parsers_data

try:
portrait_value = [
portrait_value = next(
hero["portrait"]
for hero in heroes_data
if hero["key"] == kwargs.get("hero_key")
][0]
except IndexError:
)
except StopIteration:
# The hero key may not be here in some specific edge cases,
# for example if the hero has been released but is not in the
# heroes list yet, or the list cache is outdated
portrait_value = None

# We want to insert the portrait before the "role" key
hero_data = dict_insert_value_before_key(
hero_data, "role", "portrait", portrait_value
)
else:
# We want to insert the portrait before the "role" key
hero_data = dict_insert_value_before_key(
hero_data, "role", "portrait", portrait_value
)

try:
hitpoints = heroes_stats_data[kwargs.get("hero_key")]["hitpoints"]
except KeyError:
# Hero hitpoints may not be here if the CSV file
# containing the data hasn't been updated
hitpoints = None

# We want to insert hitpoints before "abilities" key
hero_data = dict_insert_value_before_key(
hero_data, "abilities", "hitpoints", hitpoints
)
else:
# We want to insert hitpoints before "abilities" key
hero_data = dict_insert_value_before_key(
hero_data, "abilities", "hitpoints", hitpoints
)

return hero_data
4 changes: 3 additions & 1 deletion app/handlers/get_player_career_request_handler.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
"""Player Career Request Handler module"""
from typing import ClassVar

from app.config import settings
from app.parsers.namecard_parser import NamecardParser
from app.parsers.player_parser import PlayerParser
Expand All @@ -12,7 +14,7 @@ class GetPlayerCareerRequestHandler(APIRequestHandler):
PlayerParser class.
"""

parser_classes = [PlayerParser, NamecardParser]
parser_classes: ClassVar[list] = [PlayerParser, NamecardParser]
timeout = settings.career_path_cache_timeout

def merge_parsers_data(self, parsers_data: list[dict], **kwargs) -> dict:
Expand Down
4 changes: 3 additions & 1 deletion app/handlers/get_player_career_stats_request_handler.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
"""Player Stats Summary Request Handler module"""
from typing import ClassVar

from app.config import settings
from app.parsers.player_career_parser import PlayerCareerParser

Expand All @@ -10,5 +12,5 @@ class GetPlayerCareerStatsRequestHandler(APIRequestHandler):
statistics of a player without labels, easily explorable
"""

parser_classes = [PlayerCareerParser]
parser_classes: ClassVar[list] = [PlayerCareerParser]
timeout = settings.career_path_cache_timeout
4 changes: 3 additions & 1 deletion app/handlers/get_player_stats_summary_request_handler.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
"""Player Stats Summary Request Handler module"""
from typing import ClassVar

from app.config import settings
from app.parsers.player_stats_summary_parser import PlayerStatsSummaryParser

Expand All @@ -11,5 +13,5 @@ class GetPlayerStatsSummaryRequestHandler(APIRequestHandler):
Using the PlayerStatsSummaryParser.
"""

parser_classes = [PlayerStatsSummaryParser]
parser_classes: ClassVar[list] = [PlayerStatsSummaryParser]
timeout = settings.career_path_cache_timeout
4 changes: 3 additions & 1 deletion app/handlers/list_gamemodes_request_handler.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
"""List Gamemodes Request Handler module"""
from typing import ClassVar

from app.config import settings
from app.parsers.gamemodes_parser import GamemodesParser

Expand All @@ -10,5 +12,5 @@ class ListGamemodesRequestHandler(APIRequestHandler):
available Overwatch gamemodes, using the GamemodesParser class.
"""

parser_classes = [GamemodesParser]
parser_classes: ClassVar[list] = [GamemodesParser]
timeout = settings.home_path_cache_timeout
4 changes: 3 additions & 1 deletion app/handlers/list_heroes_request_handler.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
"""List Heroes Request Handler module"""
from typing import ClassVar

from app.config import settings
from app.parsers.heroes_parser import HeroesParser

Expand All @@ -10,5 +12,5 @@ class ListHeroesRequestHandler(APIRequestHandler):
retrieve a list of available Overwatch heroes.
"""

parser_classes = [HeroesParser]
parser_classes: ClassVar[list] = [HeroesParser]
timeout = settings.heroes_path_cache_timeout
4 changes: 3 additions & 1 deletion app/handlers/list_maps_request_handler.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
"""List Maps Request Handler module"""
from typing import ClassVar

from app.config import settings
from app.parsers.maps_parser import MapsParser

Expand All @@ -10,5 +12,5 @@ class ListMapsRequestHandler(APIRequestHandler):
available Overwatch maps, using the MapsParser class.
"""

parser_classes = [MapsParser]
parser_classes: ClassVar[list] = [MapsParser]
timeout = settings.home_path_cache_timeout
4 changes: 3 additions & 1 deletion app/handlers/list_roles_request_handler.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
"""List Roles Request Handler module"""
from typing import ClassVar

from app.config import settings
from app.parsers.roles_parser import RolesParser

Expand All @@ -10,5 +12,5 @@ class ListRolesRequestHandler(APIRequestHandler):
retrieve a list of available Overwatch roles.
"""

parser_classes = [RolesParser]
parser_classes: ClassVar[list] = [RolesParser]
timeout = settings.heroes_path_cache_timeout
20 changes: 11 additions & 9 deletions app/models/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,36 @@ class BlizzardErrorMessage(BaseModel):
error: str = Field(
...,
description="Message describing the error",
example="Couldn't get Blizzard page (HTTP 503 error) : Service Unavailable",
examples=["Couldn't get Blizzard page (HTTP 503 error) : Service Unavailable"],
)


class InternalServerErrorMessage(BaseModel):
error: str = Field(
...,
description="Message describing the internal server error",
example=(
"An internal server error occurred during the process. The developer "
"received a notification, but don't hesitate to create a GitHub "
"issue if you want any news concerning the bug resolution : "
"https://github.com/TeKrop/overfast-api/issues"
),
examples=[
(
"An internal server error occurred during the process. The developer "
"received a notification, but don't hesitate to create a GitHub "
"issue if you want any news concerning the bug resolution : "
"https://github.com/TeKrop/overfast-api/issues"
)
],
)


class PlayerParserErrorMessage(BaseModel):
error: str = Field(
...,
description="Message describing the player parser error",
example="Player not found",
examples=["Player not found"],
)


class HeroParserErrorMessage(BaseModel):
error: str = Field(
...,
description="Message describing the hero parser error",
example="Hero not found or not released yet",
examples=["Hero not found or not released yet"],
)
16 changes: 11 additions & 5 deletions app/models/gamemodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,27 @@ class GamemodeDetails(BaseModel):
"Key corresponding to the gamemode. Can be "
"used as filter on the maps endpoint."
),
example="push",
examples=["push"],
)
name: str = Field(..., description="Name of the gamemode", example="Push")
name: str = Field(..., description="Name of the gamemode", examples=["Push"])
icon: HttpUrl = Field(
...,
description="Icon URL of the gamemode",
example="https://blz-contentstack-images.akamaized.net/v3/assets/blt9c12f249ac15c7ec/blt054b513cd6e95acf/62fd5b4a8972f93d1e325243/Push.svg",
examples=[
"https://blz-contentstack-images.akamaized.net/v3/assets/blt9c12f249ac15c7ec/blt054b513cd6e95acf/62fd5b4a8972f93d1e325243/Push.svg"
],
)
description: str = Field(
...,
description="Description of the gamemode",
example="Teams battle to take control of a robot and push it toward the enemy base.",
examples=[
"Teams battle to take control of a robot and push it toward the enemy base."
],
)
screenshot: HttpUrl = Field(
...,
description="URL of an example screenshot of a map for the gamemode",
example="https://blz-contentstack-images.akamaized.net/v3/assets/blt9c12f249ac15c7ec/blt93eefb6e91347639/62fc2d9eda42240856c1459c/Toronto_Push.jpg",
examples=[
"https://blz-contentstack-images.akamaized.net/v3/assets/blt9c12f249ac15c7ec/blt93eefb6e91347639/62fc2d9eda42240856c1459c/Toronto_Push.jpg"
],
)
Loading

0 comments on commit 39c5466

Please sign in to comment.