Skip to content

Commit

Permalink
Maintenance: Remove Python 3.11 support.
Browse files Browse the repository at this point in the history
From now on, Python 3.12 is required.
  • Loading branch information
MikuAuahDark committed Mar 31, 2024
1 parent 5411467 commit 4ca139e
Show file tree
Hide file tree
Showing 13 changed files with 29 additions and 73 deletions.
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Null-Pointer Private Server: Revisited
[![Code Style: Black](https://img.shields.io/badge/Code%20Style-Black-000000.svg)](https://github.com/psf/black)
[![Syntax Check](https://github.com/DarkEnergyProcessor/NPPS4/actions/workflows/syntax-check.yml/badge.svg)](https://github.com/DarkEnergyProcessor/NPPS4/actions/workflows/syntax-check.yml)

WIP SIF 9.11 private server written using FastAPI.
WIP SIF 9.11 private server written in Python 3.12 using FastAPI.

Notes
-----
Expand All @@ -18,8 +18,7 @@ Notes
Requirements
-----

Codes are tested on Python 3.12. Older Python version may work but not guarantees. To contribute, Python 3.12 is
required.
NPPS4 currently supports only Python 3.12 (and possibly later version). Python 3.11 and earlier is not supported.

Install
----
Expand Down
6 changes: 1 addition & 5 deletions npps4/config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,7 @@
import importlib.util
import os
import sys

if sys.version_info >= (3, 11):
import tomllib
else:
import tomli as tomllib
import tomllib

import Cryptodome.PublicKey.RSA

Expand Down
10 changes: 5 additions & 5 deletions npps4/db/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@

import sqlalchemy.ext.asyncio

from typing import TypeVar, Any
from typing import Any

_T = TypeVar("_T", bound=common.MaybeEncrypted)


async def get_decrypted_row(session: sqlalchemy.ext.asyncio.AsyncSession, cls: type[_T], id: int) -> _T | None:
async def get_decrypted_row[
_T: common.MaybeEncrypted
](session: sqlalchemy.ext.asyncio.AsyncSession, cls: type[_T], id: int) -> _T | None:
obj = await session.get(cls, id)
return decrypt_row(session, obj)


def decrypt_row(session: sqlalchemy.ext.asyncio.AsyncSession, obj: _T | None) -> _T | None:
def decrypt_row[_T: common.MaybeEncrypted](session: sqlalchemy.ext.asyncio.AsyncSession, obj: _T | None) -> _T | None:
if obj is not None and obj._encryption_release_id is not None:
key = release_key.get(obj._encryption_release_id)

Expand Down
7 changes: 2 additions & 5 deletions npps4/download/internal.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from ..app import app
from ..config import config

from typing import TypeVar, Generic, Callable, Any
from typing import Callable, Any

_NEED_GENERATION = (1, 1)
_PLATFORM_MAP = ["iOS", "Android"]
Expand All @@ -21,10 +21,7 @@
_archive_root = _archive_root[:-1]


_T = TypeVar("_T")


class _MemoizeByModTime(Generic[_T]):
class _MemoizeByModTime[_T]:
def __init__(self, f: Callable[[str], _T]):
self.f = f
self.map: dict[str, tuple[int, _T]] = {}
Expand Down
5 changes: 1 addition & 4 deletions npps4/download/n4dlapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,7 @@ def _call_api(endpoint: str, request_data: dict[str, Any] | list[Any] | None = N
raise e from None


_T = TypeVar("_T", bound=dltype.BaseInfo)


def _fixup_links(links: list[_T], platform: int):
def _fixup_links[_T: dltype.BaseInfo](links: list[_T], platform: int):
for link in links:
if link.url.startswith("https://") and platform == 2:
# Android doesn't support HTTPS
Expand Down
14 changes: 5 additions & 9 deletions npps4/idol/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from ..app import app
from ..config import config

from typing import Annotated, Any, Callable, Coroutine, TypeVar, Generic, cast
from typing import Annotated, Any, Awaitable, Callable, TypeVar, Generic, cast


class DummyModel(pydantic.RootModel[list]):
Expand All @@ -30,14 +30,10 @@ class DummyModel(pydantic.RootModel[list]):
_U = TypeVar("_U", bound=pydantic.BaseModel)
_V = TypeVar("_V", bound=pydantic.BaseModel, covariant=True)

_EndpointWithRequestWithResponse = Callable[
[_T, _U], Coroutine[Any, Any, _V]
] # Request is pydantic, response is pydantic
_EndpointWithoutRequestWithResponse = Callable[[_T], Coroutine[Any, Any, _V]] # Request is none, response is pydantic
_EndpointWithRequestWithoutResponse = Callable[
[_T, _U], Coroutine[Any, Any, None]
] # Request is pydantic, response is none
_EndpointWithoutRequestWithoutResponse = Callable[[_T], Coroutine[Any, Any, None]] # Request is none, response is none
_EndpointWithRequestWithResponse = Callable[[_T, _U], Awaitable[_V]] # Request is pydantic, response is pydantic
_EndpointWithoutRequestWithResponse = Callable[[_T], Awaitable[_V]] # Request is none, response is pydantic
_EndpointWithRequestWithoutResponse = Callable[[_T, _U], Awaitable[None]] # Request is pydantic, response is none
_EndpointWithoutRequestWithoutResponse = Callable[[_T], Awaitable[None]] # Request is none, response is none
_PossibleEndpointFunction = (
_EndpointWithoutRequestWithResponse[_T, _V]
| _EndpointWithRequestWithResponse[_T, _U, _V]
Expand Down
5 changes: 1 addition & 4 deletions npps4/idoltype.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,12 @@ class XMCVerifyMode(enum.IntEnum):
CROSS = 2


_S = TypeVar("_S", bound=pydantic.BaseModel | list[pydantic.BaseModel])


class ReleaseInfoData(pydantic.BaseModel):
id: int
key: str


class ResponseData(pydantic.BaseModel, Generic[_S]):
class ResponseData[_S: pydantic.BaseModel](pydantic.BaseModel):
response_data: _S
release_info: list[ReleaseInfoData] = pydantic.Field(default_factory=list)
status_code: int = 200
Expand Down
5 changes: 1 addition & 4 deletions npps4/system/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@
from . import scenario_model
from . import unit_model

from typing import Generic, TypeVar

_T = TypeVar("_T")

AnyItem = (
unit_model.UnitSupportItem
Expand All @@ -18,7 +15,7 @@
)


class BeforeAfter(pydantic.BaseModel, Generic[_T]):
class BeforeAfter[_T](pydantic.BaseModel):
before: _T
after: _T

Expand Down
11 changes: 4 additions & 7 deletions npps4/system/unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from ..db import main
from ..db import unit

from typing import Literal, TypeVar, overload
from typing import Literal, overload


async def count_units(context: idol.BasicSchoolIdolContext, user: main.User, active: bool):
Expand Down Expand Up @@ -536,9 +536,6 @@ async def get_unit_stats_from_unit_data(
return stats


_T = TypeVar("_T", bound=unit_model.UnitSupportItem, contravariant=True)


async def get_unit_data_full_info(context: idol.BasicSchoolIdolContext, unit_data: main.Unit):
unit_info = await get_unit_info(context, unit_data.unit_id)
if unit_info is None:
Expand Down Expand Up @@ -743,9 +740,9 @@ async def get_removable_skill_info_request(context: idol.BasicSchoolIdolContext,
)


async def unit_to_item(
context: idol.BasicSchoolIdolContext, unit_data: main.Unit, *, cls: type[_T] = unit_model.UnitItem
):
async def unit_to_item[
_T: unit_model.UnitSupportItem
](context: idol.BasicSchoolIdolContext, unit_data: main.Unit, *, cls: type[_T] = unit_model.UnitItem):
unit_info_data = await get_unit_data_full_info(context, unit_data)
return cls.model_validate(
unit_info_data[0].model_dump() | {"add_type": ADD_TYPE.UNIT, "item_id": unit_data.unit_id, "amount": 1}
Expand Down
6 changes: 1 addition & 5 deletions npps4/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,7 @@ def datetime_to_timestamp(dt: str):
return int(dtobj.timestamp())


_T = TypeVar("_T")
_E = TypeVar("_E", bound=Exception)


def ensure_no_none(list_to: list[_T | None], exc: type[_E] = Exception, *args) -> list[_T]:
def ensure_no_none[_T, _E: Exception](list_to: list[_T | None], exc: type[_E] = Exception, *args):
if None in list_to:
raise exc(*args)

Expand Down
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,4 @@ pycryptodomex
pydantic
python-multipart
sqlalchemy[asyncio]
tomli; python_version<"3.11"
uvicorn[standard]
11 changes: 2 additions & 9 deletions scripts/list_ach_live_unlock.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,6 @@
import npps4.db.live
import npps4.idol

from typing import TypeVar, cast

_T = TypeVar("_T")


def assume_some(v: _T | None) -> _T:
return cast(_T, v)


def select_en(jp: str, en: str | None):
return en or jp
Expand All @@ -40,7 +32,8 @@ async def run_script(args: list[str]):
for ach_id in sorted(ach_trees, key=lambda k: k.achievement_id):
ach = live_clear_ach[ach_id.next_achievement_id]
live_track_id = int(ach.params1 or 0)
live_track = assume_some(await context.db.live.get(npps4.db.live.LiveTrack, live_track_id))
live_track = await context.db.live.get(npps4.db.live.LiveTrack, live_track_id)
assert live_track is not None
track_name = select_en(live_track.name, live_track.name_en)
# 110: [item.Reward(add_type=ADD_TYPE.LIVE, item_id=3)], # Reward: Snow Halation
print(
Expand Down
16 changes: 4 additions & 12 deletions scripts/list_ach_scenario_unlock.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,6 @@
import npps4.db.scenario
import npps4.idol

from typing import TypeVar, cast

_T = TypeVar("_T")


def assume_some(v: _T | None) -> _T:
return cast(_T, v)


def select_en(jp: str, en: str | None):
return en or jp
Expand All @@ -40,10 +32,10 @@ async def run_script(args: list[str]):
for ach_id in sorted(ach_trees, key=lambda k: k.achievement_id):
ach = main_story_ach[ach_id.next_achievement_id]
scenario_id = int(ach.params1 or 0)
scenario = assume_some(await context.db.scenario.get(npps4.db.scenario.Scenario, scenario_id))
scenario_chapter = assume_some(
await context.db.scenario.get(npps4.db.scenario.Chapter, scenario.scenario_chapter_id)
)
scenario = await context.db.scenario.get(npps4.db.scenario.Scenario, scenario_id)
assert scenario is not None
scenario_chapter = await context.db.scenario.get(npps4.db.scenario.Chapter, scenario.scenario_chapter_id)
assert scenario_chapter is not None
scenario_chapter_name = select_en(scenario_chapter.name, scenario_chapter.name_en)
scenario_name = select_en(scenario.title, scenario.title_en)
print(
Expand Down

0 comments on commit 4ca139e

Please sign in to comment.