From 406770f3c9343d7a0a87295bf18fe7e0a14106df Mon Sep 17 00:00:00 2001 From: seriaati Date: Sun, 22 Sep 2024 08:44:18 +0800 Subject: [PATCH] Revert "PyUpgrade unsafe fixes" This reverts commit 39597d685513e2099984648760e63991a87111ae. --- genshin-dev/setup.py | 8 +- genshin/__main__.py | 2 +- genshin/client/cache.py | 22 ++--- genshin/client/compatibility.py | 22 ++--- genshin/client/components/auth/server.py | 18 ++-- .../client/components/auth/subclients/app.py | 2 +- genshin/client/components/base.py | 10 +- .../components/calculator/calculator.py | 96 +++++++++---------- .../client/components/calculator/client.py | 74 +++++++------- genshin/client/components/chronicle/base.py | 4 +- genshin/client/components/gacha.py | 8 +- genshin/client/components/hoyolab.py | 4 +- genshin/client/components/lineup.py | 6 +- genshin/client/components/transaction.py | 6 +- genshin/client/manager/cookie.py | 2 +- genshin/client/manager/managers.py | 52 +++++----- genshin/client/ratelimit.py | 2 +- genshin/errors.py | 12 +-- genshin/models/auth/cookie.py | 6 +- genshin/models/auth/geetest.py | 4 +- genshin/models/auth/verification.py | 2 +- genshin/models/genshin/calculator.py | 16 ++-- genshin/models/genshin/character.py | 2 +- genshin/models/genshin/chronicle/abyss.py | 2 +- .../models/genshin/chronicle/activities.py | 4 +- .../models/genshin/chronicle/characters.py | 14 +-- .../models/genshin/chronicle/img_theater.py | 4 +- genshin/models/genshin/chronicle/notes.py | 4 +- genshin/models/genshin/chronicle/stats.py | 6 +- genshin/models/genshin/constants.py | 2 +- genshin/models/genshin/gacha.py | 6 +- genshin/models/genshin/lineup.py | 6 +- genshin/models/genshin/teapot.py | 6 +- genshin/models/genshin/wiki.py | 16 ++-- .../models/honkai/chronicle/battlesuits.py | 2 +- genshin/models/honkai/chronicle/modes.py | 10 +- genshin/models/honkai/chronicle/stats.py | 2 +- genshin/models/honkai/constants.py | 2 +- genshin/models/hoyolab/record.py | 14 +-- genshin/models/model.py | 2 +- .../models/starrail/chronicle/challenge.py | 28 +++--- genshin/models/starrail/chronicle/notes.py | 2 +- genshin/models/starrail/chronicle/rogue.py | 15 +-- genshin/models/zzz/chronicle/challenge.py | 18 ++-- genshin/models/zzz/chronicle/notes.py | 4 +- genshin/paginators/api.py | 32 +++---- genshin/paginators/base.py | 24 ++--- genshin/utility/auth.py | 4 +- genshin/utility/concurrency.py | 6 +- genshin/utility/ds.py | 2 +- genshin/utility/logfile.py | 4 +- tests/conftest.py | 2 +- 52 files changed, 312 insertions(+), 311 deletions(-) diff --git a/genshin-dev/setup.py b/genshin-dev/setup.py index ee204edd..712ff131 100644 --- a/genshin-dev/setup.py +++ b/genshin-dev/setup.py @@ -6,12 +6,12 @@ import setuptools -def parse_requirements_file(path: pathlib.Path) -> list[str]: +def parse_requirements_file(path: pathlib.Path) -> typing.List[str]: """Parse a requirements file into a list of requirements.""" with open(path) as fp: raw_dependencies = fp.readlines() - dependencies: list[str] = [] + dependencies: typing.List[str] = [] for dependency in raw_dependencies: comment_index = dependency.find("#") if comment_index == 0: @@ -30,8 +30,8 @@ def parse_requirements_file(path: pathlib.Path) -> list[str]: normal_requirements = parse_requirements_file(dev_directory / ".." / "requirements.txt") -all_extras: set[str] = set() -extras: dict[str, typing.Sequence[str]] = {} +all_extras: typing.Set[str] = set() +extras: typing.Dict[str, typing.Sequence[str]] = {} for path in dev_directory.glob("*-requirements.txt"): name = path.name.split("-")[0] diff --git a/genshin/__main__.py b/genshin/__main__.py index 158f830c..7a422299 100644 --- a/genshin/__main__.py +++ b/genshin/__main__.py @@ -85,7 +85,7 @@ async def honkai_stats(client: genshin.Client, uid: int) -> None: for k, v in data.stats.model_dump().items(): if isinstance(v, dict): click.echo(f"{k}:") - for nested_k, nested_v in typing.cast("dict[str, object]", v).items(): + for nested_k, nested_v in typing.cast("typing.Dict[str, object]", v).items(): click.echo(f" {nested_k}: {click.style(str(nested_v), bold=True)}") else: click.echo(f"{k}: {click.style(str(v), bold=True)}") diff --git a/genshin/client/cache.py b/genshin/client/cache.py index 1a7729ba..e31743fe 100644 --- a/genshin/client/cache.py +++ b/genshin/client/cache.py @@ -25,7 +25,7 @@ def _separate(values: typing.Iterable[typing.Any], sep: str = ":") -> str: """Separate a sequence by a separator into a single string.""" - parts: list[str] = [] + parts: typing.List[str] = [] for value in values: if value is None: parts.append("null") @@ -64,7 +64,7 @@ class BaseCache(abc.ABC): """Base cache for the client.""" @abc.abstractmethod - async def get(self, key: typing.Any) -> typing.Any | None: + async def get(self, key: typing.Any) -> typing.Optional[typing.Any]: """Get an object with a key.""" @abc.abstractmethod @@ -72,7 +72,7 @@ async def set(self, key: typing.Any, value: typing.Any) -> None: """Save an object with a key.""" @abc.abstractmethod - async def get_static(self, key: typing.Any) -> typing.Any | None: + async def get_static(self, key: typing.Any) -> typing.Optional[typing.Any]: """Get a static object with a key.""" @abc.abstractmethod @@ -83,7 +83,7 @@ async def set_static(self, key: typing.Any, value: typing.Any) -> None: class Cache(BaseCache): """Standard implementation of the cache.""" - cache: dict[typing.Any, tuple[float, typing.Any]] + cache: typing.Dict[typing.Any, typing.Tuple[float, typing.Any]] maxsize: int ttl: float static_ttl: float @@ -115,7 +115,7 @@ def _clear_cache(self) -> None: for key in keys: del self.cache[key] - async def get(self, key: typing.Any) -> typing.Any | None: + async def get(self, key: typing.Any) -> typing.Optional[typing.Any]: """Get an object with a key.""" self._clear_cache() @@ -130,7 +130,7 @@ async def set(self, key: typing.Any, value: typing.Any) -> None: self._clear_cache() - async def get_static(self, key: typing.Any) -> typing.Any | None: + async def get_static(self, key: typing.Any) -> typing.Optional[typing.Any]: """Get a static object with a key.""" return await self.get(key) @@ -167,7 +167,7 @@ def serialize_key(self, key: typing.Any) -> str: """Serialize a key by turning it into a string.""" return str(key) - def serialize_value(self, value: typing.Any) -> str | bytes: + def serialize_value(self, value: typing.Any) -> typing.Union[str, bytes]: """Serialize a value by turning it into bytes.""" return json.dumps(value) @@ -175,7 +175,7 @@ def deserialize_value(self, value: bytes) -> typing.Any: """Deserialize a value back into data.""" return json.loads(value) - async def get(self, key: typing.Any) -> typing.Any | None: + async def get(self, key: typing.Any) -> typing.Optional[typing.Any]: """Get an object with a key.""" value = typing.cast("typing.Optional[bytes]", await self.redis.get(self.serialize_key(key))) # pyright: ignore if value is None: @@ -191,7 +191,7 @@ async def set(self, key: typing.Any, value: typing.Any) -> None: ex=self.ttl, ) - async def get_static(self, key: typing.Any) -> typing.Any | None: + async def get_static(self, key: typing.Any) -> typing.Optional[typing.Any]: """Get a static object with a key.""" return await self.get(key) @@ -258,7 +258,7 @@ def deserialize_value(self, value: str) -> typing.Any: """Deserialize a value back into data.""" return json.loads(value) - async def get(self, key: typing.Any) -> typing.Any | None: + async def get(self, key: typing.Any) -> typing.Optional[typing.Any]: """Get an object with a key.""" import aiosqlite @@ -299,7 +299,7 @@ async def set(self, key: typing.Any, value: typing.Any) -> None: if self.conn is None: await conn.close() - async def get_static(self, key: typing.Any) -> typing.Any | None: + async def get_static(self, key: typing.Any) -> typing.Optional[typing.Any]: """Get a static object with a key.""" return await self.get(key) diff --git a/genshin/client/compatibility.py b/genshin/client/compatibility.py index 2f96ecce..ce4d8fc2 100644 --- a/genshin/client/compatibility.py +++ b/genshin/client/compatibility.py @@ -24,8 +24,8 @@ class GenshinClient(clients.Client): def __init__( self, - cookies: typing.Any | None = None, - authkey: str | None = None, + cookies: typing.Optional[typing.Any] = None, + authkey: typing.Optional[str] = None, *, lang: str = "en-us", region: types.Region = types.Region.OVERSEAS, @@ -59,7 +59,7 @@ def cookies(self, cookies: typing.Mapping[str, typing.Any]) -> None: setattr(self.cookie_manager, "cookies", cookies) @property - def uid(self) -> int | None: + def uid(self) -> typing.Optional[int]: deprecation.warn_deprecated(self.__class__.uid, alternative="Client.uids[genshin.Game.GENSHIN]") return self.uids[types.Game.GENSHIN] @@ -83,7 +83,7 @@ async def get_partial_user( self, uid: int, *, - lang: str | None = None, + lang: typing.Optional[str] = None, ) -> models.PartialGenshinUserStats: """Get partial genshin user without character equipment.""" return await self.get_partial_genshin_user(uid, lang=lang) @@ -93,7 +93,7 @@ async def get_characters( self, uid: int, *, - lang: str | None = None, + lang: typing.Optional[str] = None, ) -> typing.Sequence[models.Character]: """Get genshin user characters.""" return await self.get_genshin_characters(uid, lang=lang) @@ -103,7 +103,7 @@ async def get_user( self, uid: int, *, - lang: str | None = None, + lang: typing.Optional[str] = None, ) -> models.GenshinUserStats: """Get genshin user.""" return await self.get_genshin_user(uid, lang=lang) @@ -113,7 +113,7 @@ async def get_full_user( self, uid: int, *, - lang: str | None = None, + lang: typing.Optional[str] = None, ) -> models.FullGenshinUserStats: """Get a user with all their possible data.""" return await self.get_full_genshin_user(uid, lang=lang) @@ -129,8 +129,8 @@ class ChineseClient(GenshinClient): def __init__( self, - cookies: typing.Mapping[str, str] | None = None, - authkey: str | None = None, + cookies: typing.Optional[typing.Mapping[str, str]] = None, + authkey: typing.Optional[str] = None, *, lang: str = "zh-cn", debug: bool = False, @@ -154,7 +154,7 @@ class MultiCookieClient(GenshinClient): def __init__( self, - cookie_list: typing.Sequence[typing.Mapping[str, str]] | None = None, + cookie_list: typing.Optional[typing.Sequence[typing.Mapping[str, str]]] = None, *, lang: str = "en-us", debug: bool = False, @@ -176,7 +176,7 @@ class ChineseMultiCookieClient(GenshinClient): def __init__( self, - cookie_list: typing.Sequence[typing.Mapping[str, str]] | None = None, + cookie_list: typing.Optional[typing.Sequence[typing.Mapping[str, str]]] = None, *, lang: str = "en-us", debug: bool = False, diff --git a/genshin/client/components/auth/server.py b/genshin/client/components/auth/server.py index f69a3a2f..7baf267d 100644 --- a/genshin/client/components/auth/server.py +++ b/genshin/client/components/auth/server.py @@ -25,7 +25,7 @@ __all__ = ["PAGES", "enter_code", "launch_webapp", "solve_geetest"] -PAGES: typing.Final[dict[typing.Literal["captcha", "enter-code"], str]] = { +PAGES: typing.Final[typing.Dict[typing.Literal["captcha", "enter-code"], str]] = { "captcha": """ @@ -112,11 +112,11 @@ async def launch_webapp( page: typing.Literal["captcha"], *, - mmt: MMT | MMTv4 | SessionMMT | SessionMMTv4 | RiskyCheckMMT, + mmt: typing.Union[MMT, MMTv4, SessionMMT, SessionMMTv4, RiskyCheckMMT], lang: str = ..., api_server: str = ..., port: int = ..., -) -> MMTResult | MMTv4Result | SessionMMTResult | SessionMMTv4Result | RiskyCheckMMTResult: ... +) -> typing.Union[MMTResult, MMTv4Result, SessionMMTResult, SessionMMTv4Result, RiskyCheckMMTResult]: ... @typing.overload async def launch_webapp( page: typing.Literal["enter-code"], @@ -129,11 +129,11 @@ async def launch_webapp( async def launch_webapp( page: typing.Literal["captcha", "enter-code"], *, - mmt: MMT | MMTv4 | SessionMMT | SessionMMTv4 | RiskyCheckMMT | None = None, - lang: str | None = None, - api_server: str | None = None, + mmt: typing.Optional[typing.Union[MMT, MMTv4, SessionMMT, SessionMMTv4, RiskyCheckMMT]] = None, + lang: typing.Optional[str] = None, + api_server: typing.Optional[str] = None, port: int = 5000, -) -> MMTResult | MMTv4Result | SessionMMTResult | SessionMMTv4Result | RiskyCheckMMTResult | str: +) -> typing.Union[MMTResult, MMTv4Result, SessionMMTResult, SessionMMTv4Result, RiskyCheckMMTResult, str]: """Create and run a webapp to solve captcha or enter a verification code.""" routes = web.RouteTableDef() future: asyncio.Future[typing.Any] = asyncio.Future() @@ -244,12 +244,12 @@ async def solve_geetest( port: int = ..., ) -> MMTv4Result: ... async def solve_geetest( - mmt: MMT | MMTv4 | SessionMMT | SessionMMTv4 | RiskyCheckMMT, + mmt: typing.Union[MMT, MMTv4, SessionMMT, SessionMMTv4, RiskyCheckMMT], *, lang: str = "en-us", api_server: str = "api-na.geetest.com", port: int = 5000, -) -> MMTResult | MMTv4Result | SessionMMTResult | SessionMMTv4Result | RiskyCheckMMTResult: +) -> typing.Union[MMTResult, MMTv4Result, SessionMMTResult, SessionMMTv4Result, RiskyCheckMMTResult]: """Start a web server and manually solve geetest captcha.""" lang = auth_utility.lang_to_geetest_lang(lang) return await launch_webapp( diff --git a/genshin/client/components/auth/subclients/app.py b/genshin/client/components/auth/subclients/app.py index 6f0d599a..0af38edb 100644 --- a/genshin/client/components/auth/subclients/app.py +++ b/genshin/client/components/auth/subclients/app.py @@ -194,7 +194,7 @@ async def _create_qrcode(self) -> QRCodeCreationResult: url=data["data"]["url"], ) - async def _check_qrcode(self, ticket: str) -> tuple[QRCodeStatus, SimpleCookie]: + async def _check_qrcode(self, ticket: str) -> typing.Tuple[QRCodeStatus, SimpleCookie]: """Check the status of a QR code login.""" payload = {"ticket": ticket} diff --git a/genshin/client/components/base.py b/genshin/client/components/base.py index b7956c4e..851080b0 100644 --- a/genshin/client/components/base.py +++ b/genshin/client/components/base.py @@ -62,10 +62,10 @@ class BaseClient(abc.ABC): _region: types.Region _default_game: typing.Optional[types.Game] - uids: dict[types.Game, int] - authkeys: dict[types.Game, str] + uids: typing.Dict[types.Game, int] + authkeys: typing.Dict[types.Game, str] _hoyolab_id: typing.Optional[int] - _accounts: dict[types.Game, hoyolab_models.GenshinAccount] + _accounts: typing.Dict[types.Game, hoyolab_models.GenshinAccount] custom_headers: multidict.CIMultiDict[str] def __init__( @@ -500,7 +500,7 @@ async def _update_cached_uids(self) -> None: """Update cached fallback uids.""" mixed_accounts = await self.get_game_accounts() - game_accounts: dict[types.Game, list[hoyolab_models.GenshinAccount]] = {} + game_accounts: typing.Dict[types.Game, typing.List[hoyolab_models.GenshinAccount]] = {} for account in mixed_accounts: if not isinstance(account.game, types.Game): # pyright: ignore[reportUnnecessaryIsInstance] continue @@ -533,7 +533,7 @@ async def _update_cached_accounts(self) -> None: """Update cached fallback accounts.""" mixed_accounts = await self.get_game_accounts() - game_accounts: dict[types.Game, list[hoyolab_models.GenshinAccount]] = {} + game_accounts: typing.Dict[types.Game, typing.List[hoyolab_models.GenshinAccount]] = {} for account in mixed_accounts: if not isinstance(account.game, types.Game): # pyright: ignore[reportUnnecessaryIsInstance] continue diff --git a/genshin/client/components/calculator/calculator.py b/genshin/client/components/calculator/calculator.py index b08f61ca..435bd755 100644 --- a/genshin/client/components/calculator/calculator.py +++ b/genshin/client/components/calculator/calculator.py @@ -42,10 +42,10 @@ class CalculatorState: """Stores character details if multiple objects require them.""" client: Client - cache: dict[str, typing.Any] + cache: typing.Dict[str, typing.Any] lock: asyncio.Lock - character_id: int | None = None + character_id: typing.Optional[int] = None def __init__(self, client: Client) -> None: self.client = client @@ -87,10 +87,10 @@ class CharacterResolver(CalculatorResolver[typing.Mapping[str, typing.Any]]): def __init__( self, character: types.IDOr[genshin_models.BaseCharacter], - current: int | None = None, - target: int | None = None, + current: typing.Optional[int] = None, + target: typing.Optional[int] = None, *, - element: int | None = None, + element: typing.Optional[int] = None, ) -> None: if isinstance(character, genshin_models.BaseCharacter): current = current or getattr(character, "level", None) @@ -150,7 +150,7 @@ async def __call__(self, state: CalculatorState) -> typing.Mapping[str, typing.A class ArtifactResolver(CalculatorResolver[typing.Sequence[typing.Mapping[str, typing.Any]]]): - data: list[typing.Mapping[str, typing.Any]] + data: typing.List[typing.Mapping[str, typing.Any]] def __init__(self) -> None: self.data = [] @@ -180,17 +180,17 @@ async def __call__(self, state: CalculatorState) -> typing.Sequence[typing.Mappi class CurrentArtifactResolver(ArtifactResolver): - artifacts: typing.Sequence[int | None] + artifacts: typing.Sequence[typing.Optional[int]] def __init__( self, - target: int | None = None, + target: typing.Optional[int] = None, *, - flower: int | None = None, - feather: int | None = None, - sands: int | None = None, - goblet: int | None = None, - circlet: int | None = None, + flower: typing.Optional[int] = None, + feather: typing.Optional[int] = None, + sands: typing.Optional[int] = None, + goblet: typing.Optional[int] = None, + circlet: typing.Optional[int] = None, ) -> None: if target: self.artifacts = (target,) * 5 @@ -208,7 +208,7 @@ async def __call__(self, state: CalculatorState) -> typing.Sequence[typing.Mappi class TalentResolver(CalculatorResolver[typing.Sequence[typing.Mapping[str, typing.Any]]]): - data: list[typing.Mapping[str, typing.Any]] + data: typing.List[typing.Mapping[str, typing.Any]] def __init__(self) -> None: self.data = [] @@ -221,16 +221,16 @@ async def __call__(self, state: CalculatorState) -> typing.Sequence[typing.Mappi class CurrentTalentResolver(TalentResolver): - talents: typing.Mapping[str, int | None] + talents: typing.Mapping[str, typing.Optional[int]] def __init__( self, - target: int | None = None, - current: int | None = None, + target: typing.Optional[int] = None, + current: typing.Optional[int] = None, *, - attack: int | None = None, - skill: int | None = None, - burst: int | None = None, + attack: typing.Optional[int] = None, + skill: typing.Optional[int] = None, + burst: typing.Optional[int] = None, ) -> None: self.current = current if target: @@ -272,16 +272,16 @@ class Calculator: """Builder for the genshin impact enhancement calculator.""" client: Client - lang: str | None + lang: typing.Optional[str] - character: CharacterResolver | None - weapon: WeaponResolver | None - artifacts: ArtifactResolver | None - talents: TalentResolver | None + character: typing.Optional[CharacterResolver] + weapon: typing.Optional[WeaponResolver] + artifacts: typing.Optional[ArtifactResolver] + talents: typing.Optional[TalentResolver] _state: CalculatorState - def __init__(self, client: Client, *, lang: str | None = None) -> None: + def __init__(self, client: Client, *, lang: typing.Optional[str] = None) -> None: self.client = client self.lang = lang @@ -295,10 +295,10 @@ def __init__(self, client: Client, *, lang: str | None = None) -> None: def set_character( self, character: types.IDOr[genshin_models.BaseCharacter], - current: int | None = None, - target: int | None = None, + current: typing.Optional[int] = None, + target: typing.Optional[int] = None, *, - element: int | None = None, + element: typing.Optional[int] = None, ) -> Calculator: """Set the character.""" self.character = CharacterResolver(character, current, target, element=element) @@ -338,13 +338,13 @@ def with_current_weapon(self, target: int) -> Calculator: def with_current_artifacts( self, - target: int | None = None, + target: typing.Optional[int] = None, *, - flower: int | None = None, - feather: int | None = None, - sands: int | None = None, - goblet: int | None = None, - circlet: int | None = None, + flower: typing.Optional[int] = None, + feather: typing.Optional[int] = None, + sands: typing.Optional[int] = None, + goblet: typing.Optional[int] = None, + circlet: typing.Optional[int] = None, ) -> Calculator: """Add all artifacts of the selected character.""" self.artifacts = CurrentArtifactResolver( @@ -359,12 +359,12 @@ def with_current_artifacts( def with_current_talents( self, - target: int | None = None, - current: int | None = None, + target: typing.Optional[int] = None, + current: typing.Optional[int] = None, *, - attack: int | None = None, - skill: int | None = None, - burst: int | None = None, + attack: typing.Optional[int] = None, + skill: typing.Optional[int] = None, + burst: typing.Optional[int] = None, ) -> Calculator: """Add all talents of the currently selected character.""" self.talents = CurrentTalentResolver( @@ -378,7 +378,7 @@ def with_current_talents( async def build(self) -> typing.Mapping[str, typing.Any]: """Build the calculator object.""" - data: dict[str, typing.Any] = {} + data: typing.Dict[str, typing.Any] = {} if self.character: data.update(await self.character(self._state)) @@ -406,13 +406,13 @@ class FurnishingCalculator: """Builder for the genshin impact furnishing calculator.""" client: Client - lang: str | None + lang: typing.Optional[str] - furnishings: dict[int, int] - replica_code: int | None = None - replica_region: str | None = None + furnishings: typing.Dict[int, int] + replica_code: typing.Optional[int] = None + replica_region: typing.Optional[str] = None - def __init__(self, client: Client, *, lang: str | None = None) -> None: + def __init__(self, client: Client, *, lang: typing.Optional[str] = None) -> None: self.client = client self.lang = lang @@ -426,7 +426,7 @@ def add_furnishing(self, id: types.IDOr[models.CalculatorFurnishing], amount: in self.furnishings[int(id)] += amount return self - def with_replica(self, code: int, *, region: str | None = None) -> FurnishingCalculator: + def with_replica(self, code: int, *, region: typing.Optional[str] = None) -> FurnishingCalculator: """Set the replica code.""" self.replica_code = code self.replica_region = region @@ -434,7 +434,7 @@ def with_replica(self, code: int, *, region: str | None = None) -> FurnishingCal async def build(self) -> typing.Mapping[str, typing.Any]: """Build the calculator object.""" - data: dict[str, typing.Any] = {} + data: typing.Dict[str, typing.Any] = {} if self.replica_code: furnishings = await self.client.get_teapot_replica_blueprint(self.replica_code, region=self.replica_region) diff --git a/genshin/client/components/calculator/client.py b/genshin/client/components/calculator/client.py index 67017af9..2ba59a4d 100644 --- a/genshin/client/components/calculator/client.py +++ b/genshin/client/components/calculator/client.py @@ -33,10 +33,10 @@ async def request_calculator( endpoint: str, *, method: str = "POST", - lang: str | None = None, - params: typing.Mapping[str, typing.Any] | None = None, - data: typing.Mapping[str, typing.Any] | None = None, - headers: aiohttp.typedefs.LooseHeaders | None = None, + lang: typing.Optional[str] = None, + params: typing.Optional[typing.Mapping[str, typing.Any]] = None, + data: typing.Optional[typing.Mapping[str, typing.Any]] = None, + headers: typing.Optional[aiohttp.typedefs.LooseHeaders] = None, **kwargs: typing.Any, ) -> typing.Mapping[str, typing.Any]: """Make a request towards the calculator endpoint.""" @@ -70,7 +70,7 @@ async def _execute_calculator( self, data: typing.Mapping[str, typing.Any], *, - lang: str | None = None, + lang: typing.Optional[str] = None, ) -> models.CalculatorResult: """Calculate the results of a builder.""" data = await self.request_calculator("compute", lang=lang, data=data) @@ -80,17 +80,17 @@ async def _execute_furnishings_calculator( self, data: typing.Mapping[str, typing.Any], *, - lang: str | None = None, + lang: typing.Optional[str] = None, ) -> models.CalculatorFurnishingResults: """Calculate the results of a builder.""" data = await self.request_calculator("furniture/compute", lang=lang, data=data) return models.CalculatorFurnishingResults(**data) - def calculator(self, *, lang: str | None = None) -> Calculator: + def calculator(self, *, lang: typing.Optional[str] = None) -> Calculator: """Create a calculator builder object.""" return Calculator(self, lang=lang) - def furnishings_calculator(self, *, lang: str | None = None) -> FurnishingCalculator: + def furnishings_calculator(self, *, lang: typing.Optional[str] = None) -> FurnishingCalculator: """Create a calculator builder object.""" return FurnishingCalculator(self, lang=lang) @@ -102,12 +102,12 @@ async def _get_calculator_items( self, slug: str, filters: typing.Mapping[str, typing.Any], - query: str | None = None, + query: typing.Optional[str] = None, *, - uid: int | None = None, + uid: typing.Optional[int] = None, is_all: bool = False, sync: bool = False, - lang: str | None = None, + lang: typing.Optional[str] = None, autoauth: bool = True, ) -> typing.Sequence[typing.Mapping[str, typing.Any]]: """Get all items of a specific slug from a calculator.""" @@ -119,14 +119,14 @@ async def _get_calculator_items( filters = dict(keywords=query, **filters) - payload: dict[str, typing.Any] = dict(page=1, size=69420, is_all=is_all, **filters) + payload: typing.Dict[str, typing.Any] = dict(page=1, size=69420, is_all=is_all, **filters) if sync: uid = uid or await self._get_uid(types.Game.GENSHIN) payload["uid"] = uid payload["region"] = utility.recognize_genshin_server(uid) - cache: client_cache.CacheKey | None = None + cache: typing.Optional[client_cache.CacheKey] = None if not any(filters.values()) and not sync: cache = client_cache.cache_key("calculator", slug=slug, lang=lang or self.lang) @@ -146,13 +146,13 @@ async def _get_calculator_items( async def get_calculator_characters( self, *, - query: str | None = None, - elements: typing.Sequence[int] | None = None, - weapon_types: typing.Sequence[int] | None = None, + query: typing.Optional[str] = None, + elements: typing.Optional[typing.Sequence[int]] = None, + weapon_types: typing.Optional[typing.Sequence[int]] = None, include_traveler: bool = False, sync: bool = False, - uid: int | None = None, - lang: str | None = None, + uid: typing.Optional[int] = None, + lang: typing.Optional[str] = None, ) -> typing.Sequence[models.CalculatorCharacter]: """Get all characters provided by the Enhancement Progression Calculator.""" data = await self._get_calculator_items( @@ -172,10 +172,10 @@ async def get_calculator_characters( async def get_calculator_weapons( self, *, - query: str | None = None, - types: typing.Sequence[int] | None = None, - rarities: typing.Sequence[int] | None = None, - lang: str | None = None, + query: typing.Optional[str] = None, + types: typing.Optional[typing.Sequence[int]] = None, + rarities: typing.Optional[typing.Sequence[int]] = None, + lang: typing.Optional[str] = None, ) -> typing.Sequence[models.CalculatorWeapon]: """Get all weapons provided by the Enhancement Progression Calculator.""" data = await self._get_calculator_items( @@ -192,10 +192,10 @@ async def get_calculator_weapons( async def get_calculator_artifacts( self, *, - query: str | None = None, + query: typing.Optional[str] = None, pos: int = 1, - rarities: typing.Sequence[int] | None = None, - lang: str | None = None, + rarities: typing.Optional[typing.Sequence[int]] = None, + lang: typing.Optional[str] = None, ) -> typing.Sequence[models.CalculatorArtifact]: """Get all artifacts provided by the Enhancement Progression Calculator.""" data = await self._get_calculator_items( @@ -212,9 +212,9 @@ async def get_calculator_artifacts( async def get_calculator_furnishings( self, *, - types: int | None = None, - rarities: int | None = None, - lang: str | None = None, + types: typing.Optional[int] = None, + rarities: typing.Optional[int] = None, + lang: typing.Optional[str] = None, ) -> typing.Sequence[models.CalculatorFurnishing]: """Get all furnishings provided by the Enhancement Progression Calculator.""" data = await self._get_calculator_items( @@ -231,8 +231,8 @@ async def get_character_details( self, character: types.IDOr[genshin_models.BaseCharacter], *, - uid: int | None = None, - lang: str | None = None, + uid: typing.Optional[int] = None, + lang: typing.Optional[str] = None, ) -> models.CalculatorCharacterDetails: """Get the weapon, artifacts and talents of a character. @@ -257,7 +257,7 @@ async def get_character_talents( self, character: types.IDOr[genshin_models.BaseCharacter], *, - lang: str | None = None, + lang: typing.Optional[str] = None, ) -> typing.Sequence[models.CalculatorTalent]: """Get the talents of a character. @@ -274,9 +274,9 @@ async def get_character_talents( async def get_complete_artifact_set( self, - artifact: types.IDOr[genshin_models.Artifact | genshin_models.CalculatorArtifact], + artifact: types.IDOr[typing.Union[genshin_models.Artifact, genshin_models.CalculatorArtifact]], *, - lang: str | None = None, + lang: typing.Optional[str] = None, ) -> typing.Sequence[models.CalculatorArtifact]: """Get all other artifacts that share a set with any given artifact. @@ -300,9 +300,9 @@ async def get_teapot_replica_blueprint( self, share_code: int, *, - region: str | None = None, - uid: int | None = None, - lang: str | None = None, + region: typing.Optional[str] = None, + uid: typing.Optional[int] = None, + lang: typing.Optional[str] = None, ) -> typing.Sequence[models.CalculatorFurnishing]: """Get furnishings used by a teapot replica blueprint.""" if not region: @@ -319,7 +319,7 @@ async def get_teapot_replica_blueprint( return [models.CalculatorFurnishing(**i) for i in data["list"]] @deprecation.deprecated("await genshin.utility.update_characters_any()") - async def update_character_names(self, *, lang: str | None = None) -> None: + async def update_character_names(self, *, lang: typing.Optional[str] = None) -> None: """Update stored db characters with the names from the calculator.""" characters = await self.get_calculator_characters(lang=lang, include_traveler=True) diff --git a/genshin/client/components/chronicle/base.py b/genshin/client/components/chronicle/base.py index a9c34e01..fd6d9802 100644 --- a/genshin/client/components/chronicle/base.py +++ b/genshin/client/components/chronicle/base.py @@ -31,7 +31,7 @@ def __str__(self) -> str: endpoint: str uid: int lang: str - params: tuple[typing.Any, ...] = () + params: typing.Tuple[typing.Any, ...] = () class BaseBattleChronicleClient(base.BaseClient): @@ -71,7 +71,7 @@ async def request_game_record( async def get_record_cards( self, hoyolab_id: typing.Optional[int] = None, *, lang: typing.Optional[str] = None - ) -> list[models.hoyolab.RecordCard]: + ) -> typing.List[models.hoyolab.RecordCard]: """Get a user's record cards.""" hoyolab_id = hoyolab_id or self._get_hoyolab_id() diff --git a/genshin/client/components/gacha.py b/genshin/client/components/gacha.py index bb9f3a08..b45af6a4 100644 --- a/genshin/client/components/gacha.py +++ b/genshin/client/components/gacha.py @@ -57,7 +57,7 @@ async def _get_gacha_page( game: typing.Optional[types.Game] = None, lang: typing.Optional[str] = None, authkey: typing.Optional[str] = None, - ) -> tuple[typing.Sequence[typing.Any], int]: + ) -> typing.Tuple[typing.Sequence[typing.Any], int]: """Get a single page of wishes.""" data = await self.request_gacha_info( "getGachaLog", @@ -150,7 +150,7 @@ def wish_history( if not isinstance(banner_types, typing.Sequence): banner_types = [banner_types] - iterators: list[paginators.Paginator[models.Wish]] = [] + iterators: typing.List[paginators.Paginator[models.Wish]] = [] for banner in banner_types: iterators.append( paginators.CursorPaginator( @@ -185,7 +185,7 @@ def warp_history( if not isinstance(banner_types, typing.Sequence): banner_types = [banner_types] - iterators: list[paginators.Paginator[models.Warp]] = [] + iterators: typing.List[paginators.Paginator[models.Warp]] = [] for banner in banner_types: iterators.append( paginators.CursorPaginator( @@ -220,7 +220,7 @@ def signal_history( if not isinstance(banner_types, typing.Sequence): banner_types = [banner_types] - iterators: list[paginators.Paginator[models.SignalSearch]] = [] + iterators: typing.List[paginators.Paginator[models.SignalSearch]] = [] for banner in banner_types: iterators.append( paginators.CursorPaginator( diff --git a/genshin/client/components/hoyolab.py b/genshin/client/components/hoyolab.py index b781e49f..3df6e2e4 100644 --- a/genshin/client/components/hoyolab.py +++ b/genshin/client/components/hoyolab.py @@ -94,8 +94,8 @@ async def _request_announcements( ), ) - announcements: list[typing.Mapping[str, typing.Any]] = [] - extra_list: list[typing.Mapping[str, typing.Any]] = ( + announcements: typing.List[typing.Mapping[str, typing.Any]] = [] + extra_list: typing.List[typing.Mapping[str, typing.Any]] = ( info["pic_list"][0]["type_list"] if "pic_list" in info and info["pic_list"] else [] ) for sublist in info["list"] + extra_list: diff --git a/genshin/client/components/lineup.py b/genshin/client/components/lineup.py index a9dc5f4e..2ca3c8c9 100644 --- a/genshin/client/components/lineup.py +++ b/genshin/client/components/lineup.py @@ -56,7 +56,7 @@ async def get_lineup_scenarios( lang=lang, static_cache=cache.cache_key("lineup", endpoint="tags", lang=lang or self.lang), ) - dummy: dict[str, typing.Any] = dict(id=0, name="", children=data["tree"]) + dummy: typing.Dict[str, typing.Any] = dict(id=0, name="", children=data["tree"]) return models.LineupScenarios(**dummy) @@ -70,9 +70,9 @@ async def _get_lineup_page( order: typing.Optional[str] = None, uid: typing.Optional[int] = None, lang: typing.Optional[str] = None, - ) -> tuple[str, typing.Sequence[models.LineupPreview]]: + ) -> typing.Tuple[str, typing.Sequence[models.LineupPreview]]: """Get a single page of lineups.""" - params: dict[str, typing.Any] = dict( + params: typing.Dict[str, typing.Any] = dict( next_page_token=token, limit=limit or "", tag_id=tag_id or "", diff --git a/genshin/client/components/transaction.py b/genshin/client/components/transaction.py index 61c8e00d..d74099fe 100644 --- a/genshin/client/components/transaction.py +++ b/genshin/client/components/transaction.py @@ -61,10 +61,10 @@ async def _get_transaction_page( params=dict(end_id=end_id, size=20), ) - transactions: list[models.BaseTransaction] = [] + transactions: typing.List[models.BaseTransaction] = [] for trans in data["list"]: model = models.ItemTransaction if "name" in trans else models.Transaction - model = typing.cast("type[models.BaseTransaction]", model) + model = typing.cast("typing.Type[models.BaseTransaction]", model) transactions.append(model(**trans, kind=kind)) return transactions @@ -84,7 +84,7 @@ def transaction_log( if isinstance(kinds, str): kinds = [kinds] - iterators: list[paginators.Paginator[models.BaseTransaction]] = [] + iterators: typing.List[paginators.Paginator[models.BaseTransaction]] = [] for kind in kinds: iterators.append( paginators.CursorPaginator( diff --git a/genshin/client/manager/cookie.py b/genshin/client/manager/cookie.py index c70b2537..2ee60d5b 100644 --- a/genshin/client/manager/cookie.py +++ b/genshin/client/manager/cookie.py @@ -86,7 +86,7 @@ async def fetch_cookie_with_cookie( async def fetch_cookie_with_stoken_v2( cookies: managers.CookieOrHeader, *, - token_types: list[typing.Literal[2, 4]], + token_types: typing.List[typing.Literal[2, 4]], ) -> typing.Mapping[str, str]: """Fetch cookie (v2) with an stoken (v2) and mid.""" cookies = managers.parse_cookie(cookies) diff --git a/genshin/client/manager/managers.py b/genshin/client/manager/managers.py index e0b407b5..9f7c1004 100644 --- a/genshin/client/manager/managers.py +++ b/genshin/client/manager/managers.py @@ -36,7 +36,7 @@ MaybeSequence = typing.Union[T, typing.Sequence[T]] -def parse_cookie(cookie: CookieOrHeader | None) -> dict[str, str]: +def parse_cookie(cookie: typing.Optional[CookieOrHeader]) -> typing.Dict[str, str]: """Parse a cookie or header into a cookie mapping.""" if cookie is None: return {} @@ -47,7 +47,7 @@ def parse_cookie(cookie: CookieOrHeader | None) -> dict[str, str]: return {str(k): v.value if isinstance(v, http.cookies.Morsel) else str(v) for k, v in cookie.items()} -def get_cookie_identifier(cookie: typing.Mapping[str, str]) -> str | None: +def get_cookie_identifier(cookie: typing.Mapping[str, str]) -> typing.Optional[str]: """Get a unique identifier for a cookie.""" for name, value in cookie.items(): if name in ("ltuid", "account_id", "ltuid_v2", "account_id_v2"): @@ -64,11 +64,11 @@ def get_cookie_identifier(cookie: typing.Mapping[str, str]) -> str | None: class BaseCookieManager(abc.ABC): """A cookie manager for making requests.""" - _proxy: yarl.URL | None = None - _socks_proxy: str | None = None + _proxy: typing.Optional[yarl.URL] = None + _socks_proxy: typing.Optional[str] = None @classmethod - def from_cookies(cls, cookies: AnyCookieOrHeader | None = None) -> BaseCookieManager: + def from_cookies(cls, cookies: typing.Optional[AnyCookieOrHeader] = None) -> BaseCookieManager: """Create an arbitrary cookie manager implementation instance.""" if not cookies: return CookieManager() @@ -79,7 +79,7 @@ def from_cookies(cls, cookies: AnyCookieOrHeader | None = None) -> BaseCookieMan return CookieManager(cookies) @classmethod - def from_browser_cookies(cls, browser: str | None = None) -> CookieManager: + def from_browser_cookies(cls, browser: typing.Optional[str] = None) -> CookieManager: """Create a cookie manager with browser cookies.""" manager = CookieManager() manager.set_browser_cookies(browser) @@ -97,7 +97,7 @@ def multi(self) -> bool: return False @property - def user_id(self) -> int | None: + def user_id(self) -> typing.Optional[int]: """The id of the user that owns cookies. Returns None if not found or not applicable. @@ -105,12 +105,12 @@ def user_id(self) -> int | None: return None @property - def proxy(self) -> yarl.URL | None: + def proxy(self) -> typing.Optional[yarl.URL]: """Proxy for http(s) requests.""" return self._proxy @proxy.setter - def proxy(self, proxy: aiohttp.typedefs.StrOrURL | None) -> None: + def proxy(self, proxy: typing.Optional[aiohttp.typedefs.StrOrURL]) -> None: if proxy is None: self._proxy = None self._socks_proxy = None @@ -179,11 +179,11 @@ async def request( url: aiohttp.typedefs.StrOrURL, *, method: str = "GET", - params: typing.Mapping[str, typing.Any] | None = None, + params: typing.Optional[typing.Mapping[str, typing.Any]] = None, data: typing.Any = None, json: typing.Any = None, - cookies: aiohttp.typedefs.LooseCookies | None = None, - headers: aiohttp.typedefs.LooseHeaders | None = None, + cookies: typing.Optional[aiohttp.typedefs.LooseCookies] = None, + headers: typing.Optional[aiohttp.typedefs.LooseHeaders] = None, **kwargs: typing.Any, ) -> typing.Any: """Make an authenticated request.""" @@ -192,11 +192,11 @@ async def request( class CookieManager(BaseCookieManager): """Standard implementation of the cookie manager.""" - _cookies: dict[str, str] + _cookies: typing.Dict[str, str] def __init__( self, - cookies: CookieOrHeader | None = None, + cookies: typing.Optional[CookieOrHeader] = None, ) -> None: self.cookies = parse_cookie(cookies) @@ -209,7 +209,7 @@ def cookies(self) -> typing.MutableMapping[str, str]: return self._cookies @cookies.setter - def cookies(self, cookies: CookieOrHeader | None) -> None: + def cookies(self, cookies: typing.Optional[CookieOrHeader]) -> None: if not cookies: self._cookies = {} return @@ -239,7 +239,7 @@ def header(self) -> str: def set_cookies( self, - cookies: CookieOrHeader | None = None, + cookies: typing.Optional[CookieOrHeader] = None, **kwargs: typing.Any, ) -> typing.MutableMapping[str, str]: """Parse and set cookies.""" @@ -249,7 +249,7 @@ def set_cookies( self.cookies = parse_cookie(cookies or kwargs) return self.cookies - def set_browser_cookies(self, browser: str | None = None) -> typing.Mapping[str, str]: + def set_browser_cookies(self, browser: typing.Optional[str] = None) -> typing.Mapping[str, str]: """Extract cookies from your browser and set them as client cookies. Available browsers: chrome, chromium, opera, edge, firefox. @@ -258,7 +258,7 @@ def set_browser_cookies(self, browser: str | None = None) -> typing.Mapping[str, return self.cookies @property - def user_id(self) -> int | None: + def user_id(self) -> typing.Optional[int]: """The id of the user that owns cookies. Returns None if cookies are not set. @@ -287,9 +287,9 @@ class CookieSequence(typing.Sequence[typing.Mapping[str, str]]): MAX_USES: int = 30 # {id: ({cookie}, uses), ...} - _cookies: dict[str, tuple[dict[str, str], int]] + _cookies: typing.Dict[str, typing.Tuple[typing.Dict[str, str], int]] - def __init__(self, cookies: typing.Sequence[CookieOrHeader] | None = None) -> None: + def __init__(self, cookies: typing.Optional[typing.Sequence[CookieOrHeader]] = None) -> None: self.cookies = [parse_cookie(cookie) for cookie in cookies or []] @property @@ -299,7 +299,7 @@ def cookies(self) -> typing.Sequence[typing.Mapping[str, str]]: return [cookies for cookies, _ in cookies] @cookies.setter - def cookies(self, cookies: typing.Sequence[CookieOrHeader] | None) -> None: + def cookies(self, cookies: typing.Optional[typing.Sequence[CookieOrHeader]]) -> None: if not cookies: self._cookies = {} return @@ -335,7 +335,7 @@ class RotatingCookieManager(BaseCookieManager): _cookies: CookieSequence - def __init__(self, cookies: typing.Sequence[CookieOrHeader] | None = None) -> None: + def __init__(self, cookies: typing.Optional[typing.Sequence[CookieOrHeader]] = None) -> None: self.set_cookies(cookies) @property @@ -344,7 +344,7 @@ def cookies(self) -> typing.Sequence[typing.Mapping[str, str]]: return self._cookies @cookies.setter - def cookies(self, cookies: typing.Sequence[CookieOrHeader] | None) -> None: + def cookies(self, cookies: typing.Optional[typing.Sequence[CookieOrHeader]]) -> None: self._cookies.cookies = cookies # type: ignore # mypy does not understand property setters def __repr__(self) -> str: @@ -360,7 +360,7 @@ def multi(self) -> bool: def set_cookies( self, - cookies: typing.Sequence[CookieOrHeader] | None = None, + cookies: typing.Optional[typing.Sequence[CookieOrHeader]] = None, ) -> typing.Sequence[typing.Mapping[str, str]]: """Parse and set cookies.""" self._cookies = CookieSequence(cookies) @@ -401,7 +401,7 @@ class InternationalCookieManager(BaseCookieManager): _cookies: typing.Mapping[types.Region, CookieSequence] - def __init__(self, cookies: typing.Mapping[str, MaybeSequence[CookieOrHeader]] | None = None) -> None: + def __init__(self, cookies: typing.Optional[typing.Mapping[str, MaybeSequence[CookieOrHeader]]] = None) -> None: self.set_cookies(cookies) @property @@ -422,7 +422,7 @@ def multi(self) -> bool: def set_cookies( self, - cookies: typing.Mapping[str, MaybeSequence[CookieOrHeader]] | None = None, + cookies: typing.Optional[typing.Mapping[str, MaybeSequence[CookieOrHeader]]] = None, ) -> typing.Mapping[types.Region, typing.Sequence[typing.Mapping[str, str]]]: """Parse and set cookies.""" self._cookies = {} diff --git a/genshin/client/ratelimit.py b/genshin/client/ratelimit.py index 7abec596..61ef328d 100644 --- a/genshin/client/ratelimit.py +++ b/genshin/client/ratelimit.py @@ -11,7 +11,7 @@ def handle_ratelimits( tries: int = 5, - exception: type[errors.GenshinException] = errors.VisitsTooFrequently, + exception: typing.Type[errors.GenshinException] = errors.VisitsTooFrequently, delay: float = 0.3, ) -> typing.Callable[[CallableT], CallableT]: """Handle ratelimits for requests.""" diff --git a/genshin/errors.py b/genshin/errors.py index 67e648e9..71724114 100644 --- a/genshin/errors.py +++ b/genshin/errors.py @@ -188,7 +188,7 @@ class WrongOTP(GenshinException): class GeetestError(GenshinException): """Geetest triggered during the battle chronicle API request.""" - def __init__(self, response: dict[str, typing.Any]) -> None: + def __init__(self, response: typing.Dict[str, typing.Any]) -> None: super().__init__(response) msg = "Geetest triggered during the battle chronicle API request." @@ -232,8 +232,8 @@ class VerificationCodeRateLimited(GenshinException): msg = "Too many verification code requests for the account." -_TGE = type[GenshinException] -_errors: dict[int, typing.Union[_TGE, str, tuple[_TGE, typing.Optional[str]]]] = { +_TGE = typing.Type[GenshinException] +_errors: typing.Dict[int, typing.Union[_TGE, str, typing.Tuple[_TGE, typing.Optional[str]]]] = { # misc hoyolab -100: InvalidCookies, -108: "Invalid language.", @@ -286,13 +286,13 @@ class VerificationCodeRateLimited(GenshinException): -202: IncorrectGamePassword, } -ERRORS: dict[int, tuple[_TGE, typing.Optional[str]]] = { +ERRORS: typing.Dict[int, typing.Tuple[_TGE, typing.Optional[str]]] = { retcode: (GenshinException, exc) if isinstance(exc, str) else exc if isinstance(exc, tuple) else (exc, None) for retcode, exc in _errors.items() } -def raise_for_retcode(data: dict[str, typing.Any]) -> typing.NoReturn: +def raise_for_retcode(data: typing.Dict[str, typing.Any]) -> typing.NoReturn: """Raise an equivalent error to a response. game record: @@ -327,7 +327,7 @@ def raise_for_retcode(data: dict[str, typing.Any]) -> typing.NoReturn: raise GenshinException(data) -def check_for_geetest(data: dict[str, typing.Any]) -> None: +def check_for_geetest(data: typing.Dict[str, typing.Any]) -> None: """Check if geetest was triggered during the request and raise an error if so.""" if data["retcode"] in GEETEST_RETCODES: raise GeetestError(data) diff --git a/genshin/models/auth/cookie.py b/genshin/models/auth/cookie.py index 59549ccd..9c0ef4ba 100644 --- a/genshin/models/auth/cookie.py +++ b/genshin/models/auth/cookie.py @@ -26,7 +26,7 @@ class StokenResult(pydantic.BaseModel): token: str @pydantic.model_validator(mode="before") - def _transform_result(cls, values: dict[str, typing.Any]) -> dict[str, typing.Any]: + def _transform_result(cls, values: typing.Dict[str, typing.Any]) -> typing.Dict[str, typing.Any]: return { "aid": values["user_info"]["aid"], "mid": values["user_info"]["mid"], @@ -41,7 +41,7 @@ def to_str(self) -> str: """Convert the login cookies to a string.""" return "; ".join(f"{key}={value}" for key, value in self.model_dump().items()) - def to_dict(self) -> dict[str, str]: + def to_dict(self) -> typing.Dict[str, str]: """Convert the login cookies to a dictionary.""" return self.model_dump() @@ -121,7 +121,7 @@ class DeviceGrantResult(pydantic.BaseModel): login_ticket: typing.Optional[str] = None @pydantic.model_validator(mode="before") - def _str_to_none(cls, data: dict[str, typing.Union[str, None]]) -> dict[str, typing.Union[str, None]]: + def _str_to_none(cls, data: typing.Dict[str, typing.Union[str, None]]) -> typing.Dict[str, typing.Union[str, None]]: """Convert empty strings to `None`.""" for key in data: if data[key] == "" or data[key] == "None": diff --git a/genshin/models/auth/geetest.py b/genshin/models/auth/geetest.py index cf62f07a..1340f3a5 100644 --- a/genshin/models/auth/geetest.py +++ b/genshin/models/auth/geetest.py @@ -32,7 +32,7 @@ class BaseMMT(pydantic.BaseModel): success: int @pydantic.model_validator(mode="before") - def __parse_data(cls, data: dict[str, typing.Any]) -> dict[str, typing.Any]: + def __parse_data(cls, data: typing.Dict[str, typing.Any]) -> typing.Dict[str, typing.Any]: """Parse the data if it was provided in a raw format.""" if "data" in data: # Assume the data is aigis header and parse it @@ -89,7 +89,7 @@ class RiskyCheckMMT(MMT): class BaseMMTResult(pydantic.BaseModel): """Base Geetest verification result model.""" - def get_data(self) -> dict[str, typing.Any]: + def get_data(self) -> typing.Dict[str, typing.Any]: """Get the base MMT result data. This method acts as `dict` but excludes the `session_id` field. diff --git a/genshin/models/auth/verification.py b/genshin/models/auth/verification.py index 1c738c22..5d51a573 100644 --- a/genshin/models/auth/verification.py +++ b/genshin/models/auth/verification.py @@ -25,7 +25,7 @@ class ActionTicket(pydantic.BaseModel): verify_str: VerifyStrategy @pydantic.model_validator(mode="before") - def __parse_data(cls, data: dict[str, typing.Any]) -> dict[str, typing.Any]: + def __parse_data(cls, data: typing.Dict[str, typing.Any]) -> typing.Dict[str, typing.Any]: """Parse the data if it was provided in a raw format.""" verify_str = data["verify_str"] if isinstance(verify_str, str): diff --git a/genshin/models/genshin/calculator.py b/genshin/models/genshin/calculator.py index 82467acd..c9945c44 100644 --- a/genshin/models/genshin/calculator.py +++ b/genshin/models/genshin/calculator.py @@ -168,7 +168,7 @@ class CalculatorFurnishing(APIModel, Unique): icon: str = Aliased("icon_url") rarity: int = Aliased("level") - amount: int | None = Aliased("num") + amount: typing.Optional[int] = Aliased("num") class CalculatorCharacterDetails(APIModel): @@ -181,7 +181,7 @@ class CalculatorCharacterDetails(APIModel): @pydantic.field_validator("talents") def __correct_talent_current_level(cls, v: typing.Sequence[CalculatorTalent]) -> typing.Sequence[CalculatorTalent]: # passive talent have current levels at 0 for some reason - talents: list[CalculatorTalent] = [] + talents: typing.List[CalculatorTalent] = [] for talent in v: if talent.max_level == 1 and talent.level == 0: @@ -221,17 +221,17 @@ class CalculatorArtifactResult(APIModel): class CalculatorResult(APIModel): """Calculation result.""" - character: list[CalculatorConsumable] = Aliased("avatar_consume") - weapon: list[CalculatorConsumable] = Aliased("weapon_consume") - talents: list[CalculatorConsumable] = Aliased("avatar_skill_consume") - artifacts: list[CalculatorArtifactResult] = Aliased("reliquary_consume") + character: typing.List[CalculatorConsumable] = Aliased("avatar_consume") + weapon: typing.List[CalculatorConsumable] = Aliased("weapon_consume") + talents: typing.List[CalculatorConsumable] = Aliased("avatar_skill_consume") + artifacts: typing.List[CalculatorArtifactResult] = Aliased("reliquary_consume") @property def total(self) -> typing.Sequence[CalculatorConsumable]: artifacts = [i for a in self.artifacts for i in a.list] combined = self.character + self.weapon + self.talents + artifacts - grouped: dict[int, list[CalculatorConsumable]] = collections.defaultdict(list) + grouped: typing.Dict[int, typing.List[CalculatorConsumable]] = collections.defaultdict(list) for i in combined: grouped[i.id].append(i) @@ -251,7 +251,7 @@ def total(self) -> typing.Sequence[CalculatorConsumable]: class CalculatorFurnishingResults(APIModel): """Furnishing calculation result.""" - furnishings: list[CalculatorConsumable] = Aliased("list") + furnishings: typing.List[CalculatorConsumable] = Aliased("list") @property def total(self) -> typing.Sequence[CalculatorConsumable]: diff --git a/genshin/models/genshin/character.py b/genshin/models/genshin/character.py index 797e5d2a..1b5e5b1d 100644 --- a/genshin/models/genshin/character.py +++ b/genshin/models/genshin/character.py @@ -132,7 +132,7 @@ class BaseCharacter(APIModel, Unique): collab: bool = False @pydantic.model_validator(mode="before") - def __autocomplete(cls, values: dict[str, typing.Any]) -> dict[str, typing.Any]: + def __autocomplete(cls, values: typing.Dict[str, typing.Any]) -> typing.Dict[str, typing.Any]: """Complete missing data.""" all_fields = list(cls.model_fields.keys()) all_aliases = {f: cls.model_fields[f].alias for f in all_fields if cls.model_fields[f].alias} diff --git a/genshin/models/genshin/chronicle/abyss.py b/genshin/models/genshin/chronicle/abyss.py index ad03da31..2aa1330a 100644 --- a/genshin/models/genshin/chronicle/abyss.py +++ b/genshin/models/genshin/chronicle/abyss.py @@ -91,7 +91,7 @@ class SpiralAbyss(APIModel): floors: typing.Sequence[Floor] @pydantic.model_validator(mode="before") - def __nest_ranks(cls, values: dict[str, typing.Any]) -> dict[str, AbyssCharacter]: + def __nest_ranks(cls, values: typing.Dict[str, typing.Any]) -> typing.Dict[str, AbyssCharacter]: """By default ranks are for some reason on the same level as the rest of the abyss.""" values.setdefault("ranks", {}).update(values) return values diff --git a/genshin/models/genshin/chronicle/activities.py b/genshin/models/genshin/chronicle/activities.py index 3a6c9271..1655b713 100644 --- a/genshin/models/genshin/chronicle/activities.py +++ b/genshin/models/genshin/chronicle/activities.py @@ -27,7 +27,7 @@ class OldActivity(APIModel, typing.Generic[ModelT]): """Arbitrary activity for chinese events.""" # sometimes __parameters__ may not be provided in older versions - __parameters__: typing.ClassVar[tuple[typing.Any, ...]] = (ModelT,) # type: ignore + __parameters__: typing.ClassVar[typing.Tuple[typing.Any, ...]] = (ModelT,) # type: ignore exists_data: bool records: typing.Sequence[ModelT] @@ -310,7 +310,7 @@ class Activities(APIModel): chess: typing.Optional[Activity[typing.Any]] = None @pydantic.model_validator(mode="before") - def __flatten_activities(cls, values: dict[str, typing.Any]) -> dict[str, typing.Any]: + def __flatten_activities(cls, values: typing.Dict[str, typing.Any]) -> typing.Dict[str, typing.Any]: if not values.get("activities"): return values diff --git a/genshin/models/genshin/chronicle/characters.py b/genshin/models/genshin/chronicle/characters.py index 9d2d1043..1dba8ecf 100644 --- a/genshin/models/genshin/chronicle/characters.py +++ b/genshin/models/genshin/chronicle/characters.py @@ -127,7 +127,7 @@ class Character(PartialCharacter): @pydantic.field_validator("artifacts") @classmethod def __enable_artifact_set_effects(cls, artifacts: typing.Sequence[Artifact]) -> typing.Sequence[Artifact]: - set_nums: defaultdict[int, int] = defaultdict(int) + set_nums: typing.DefaultDict[int, int] = defaultdict(int) for arti in artifacts: set_nums[arti.set.id] += 1 @@ -244,16 +244,16 @@ class GenshinDetailCharacters(APIModel): avatar_wiki: typing.Mapping[str, str] @pydantic.model_validator(mode="before") - def __fill_prop_info(cls, values: dict[str, typing.Any]) -> typing.Mapping[str, typing.Any]: + def __fill_prop_info(cls, values: typing.Dict[str, typing.Any]) -> typing.Mapping[str, typing.Any]: """Fill property info from properety_map.""" - relic_property_options: dict[str, list[int]] = values.get("relic_property_options", {}) - prop_map: dict[str, dict[str, typing.Any]] = values.get("property_map", {}) - characters: list[dict[str, typing.Any]] = values.get("list", []) + relic_property_options: typing.Dict[str, list[int]] = values.get("relic_property_options", {}) + prop_map: typing.Dict[str, typing.Dict[str, typing.Any]] = values.get("property_map", {}) + characters: list[typing.Dict[str, typing.Any]] = values.get("list", []) # Map properties to artifacts - new_relic_prop_options: dict[str, list[dict[str, typing.Any]]] = {} + new_relic_prop_options: typing.Dict[str, list[typing.Dict[str, typing.Any]]] = {} for relic_type, properties in relic_property_options.items(): - formatted_properties: list[dict[str, typing.Any]] = [ + formatted_properties: list[typing.Dict[str, typing.Any]] = [ prop_map[str(prop)] for prop in properties if str(prop) in prop_map ] new_relic_prop_options[relic_type] = formatted_properties diff --git a/genshin/models/genshin/chronicle/img_theater.py b/genshin/models/genshin/chronicle/img_theater.py index c4133b1a..77675e18 100644 --- a/genshin/models/genshin/chronicle/img_theater.py +++ b/genshin/models/genshin/chronicle/img_theater.py @@ -145,8 +145,8 @@ class ImgTheaterData(APIModel): battle_stats: TheaterBattleStats = Aliased("fight_statisic", default=None) @pydantic.model_validator(mode="before") - def __unnest_detail(cls, values: dict[str, typing.Any]) -> dict[str, typing.Any]: - detail: typing.Optional[dict[str, typing.Any]] = values.get("detail") + def __unnest_detail(cls, values: typing.Dict[str, typing.Any]) -> typing.Dict[str, typing.Any]: + detail: typing.Optional[typing.Dict[str, typing.Any]] = values.get("detail") values["rounds_data"] = detail.get("rounds_data", []) if detail is not None else [] values["backup_avatars"] = detail.get("backup_avatars", []) if detail is not None else [] values["fight_statisic"] = detail.get("fight_statisic", None) if detail is not None else None diff --git a/genshin/models/genshin/chronicle/notes.py b/genshin/models/genshin/chronicle/notes.py index 8c66943b..41d045be 100644 --- a/genshin/models/genshin/chronicle/notes.py +++ b/genshin/models/genshin/chronicle/notes.py @@ -63,7 +63,7 @@ class TransformerTimedelta(datetime.timedelta): """Transformer recovery time.""" @property - def timedata(self) -> tuple[int, int, int, int]: + def timedata(self) -> typing.Tuple[int, int, int, int]: seconds: int = super().seconds days: int = super().days hour, second = divmod(seconds, 3600) @@ -219,7 +219,7 @@ def transformer_recovery_time(self) -> typing.Optional[datetime.datetime]: return remaining @pydantic.model_validator(mode="before") - def __flatten_transformer(cls, values: dict[str, typing.Any]) -> dict[str, typing.Any]: + def __flatten_transformer(cls, values: typing.Dict[str, typing.Any]) -> typing.Dict[str, typing.Any]: if "transformer_recovery_time" in values: return values diff --git a/genshin/models/genshin/chronicle/stats.py b/genshin/models/genshin/chronicle/stats.py index f747c602..a4402978 100644 --- a/genshin/models/genshin/chronicle/stats.py +++ b/genshin/models/genshin/chronicle/stats.py @@ -114,7 +114,7 @@ class Exploration(APIModel): offerings: typing.Sequence[Offering] boss_list: typing.Sequence[BossKill] area_exploration_list: typing.Sequence[AreaExploration] - natlan_reputation: NatlanReputation | None = Aliased("natan_reputation", default=None) + natlan_reputation: typing.Optional[NatlanReputation] = Aliased("natan_reputation", default=None) @property def explored(self) -> float: @@ -162,10 +162,10 @@ class PartialGenshinUserStats(APIModel): stats: Stats characters: typing.Sequence[characters_module.PartialCharacter] = Aliased("avatars") explorations: typing.Sequence[Exploration] = Aliased("world_explorations") - teapot: Teapot | None = Aliased("homes") + teapot: typing.Optional[Teapot] = Aliased("homes") @pydantic.field_validator("teapot", mode="before") - def __format_teapot(cls, v: typing.Any) -> dict[str, typing.Any] | None: + def __format_teapot(cls, v: typing.Any) -> typing.Optional[typing.Dict[str, typing.Any]]: if not v: return None if isinstance(v, dict): diff --git a/genshin/models/genshin/constants.py b/genshin/models/genshin/constants.py index 4ee63743..3b5cc134 100644 --- a/genshin/models/genshin/constants.py +++ b/genshin/models/genshin/constants.py @@ -92,4 +92,4 @@ class DBChar(typing.NamedTuple): # 10000071: ("Cyno", "Electro", 5), # 10000072: ("Candace", "Hydro", 4), # } -CHARACTER_NAMES: dict[str, dict[int, DBChar]] = {} +CHARACTER_NAMES: typing.Dict[str, typing.Dict[int, DBChar]] = {} diff --git a/genshin/models/genshin/gacha.py b/genshin/models/genshin/gacha.py index a5e0d133..e6acbdb8 100644 --- a/genshin/models/genshin/gacha.py +++ b/genshin/models/genshin/gacha.py @@ -192,9 +192,9 @@ class BannerDetails(APIModel): r5_up_items: typing.Sequence[BannerDetailsUpItem] r4_up_items: typing.Sequence[BannerDetailsUpItem] - r5_items: list[BannerDetailItem] = Aliased("r5_prob_list") - r4_items: list[BannerDetailItem] = Aliased("r4_prob_list") - r3_items: list[BannerDetailItem] = Aliased("r3_prob_list") + r5_items: typing.List[BannerDetailItem] = Aliased("r5_prob_list") + r4_items: typing.List[BannerDetailItem] = Aliased("r4_prob_list") + r3_items: typing.List[BannerDetailItem] = Aliased("r3_prob_list") @pydantic.field_validator("r5_up_items", "r4_up_items", mode="before") def __replace_none(cls, v: typing.Optional[typing.Sequence[typing.Any]]) -> typing.Sequence[typing.Any]: diff --git a/genshin/models/genshin/lineup.py b/genshin/models/genshin/lineup.py index 696b8aaf..ef6c0792 100644 --- a/genshin/models/genshin/lineup.py +++ b/genshin/models/genshin/lineup.py @@ -122,7 +122,7 @@ class LineupArtifactStatFields(APIModel): secondary_stats: typing.Mapping[int, str] = Aliased("reliquary_sec_attr") @pydantic.model_validator(mode="before") - def __flatten_stats(cls, values: dict[str, typing.Any]) -> dict[str, typing.Any]: + def __flatten_stats(cls, values: typing.Dict[str, typing.Any]) -> typing.Dict[str, typing.Any]: """Name certain stats.""" if "reliquary_fst_attr" not in values: return values @@ -143,7 +143,7 @@ def __flatten_stats(cls, values: dict[str, typing.Any]) -> dict[str, typing.Any] return values @pydantic.field_validator("secondary_stats", "flower", "plume", "sands", "goblet", "circlet", mode="before") - def __parse_secondary_stats(cls, value: typing.Any) -> dict[int, str]: + def __parse_secondary_stats(cls, value: typing.Any) -> typing.Dict[int, str]: if not isinstance(value, typing.Sequence): return value @@ -186,7 +186,7 @@ class LineupScenario(APIModel, Unique): children: typing.Sequence[LineupScenario] @pydantic.model_validator(mode="before") - def __flatten_scenarios(cls, values: dict[str, typing.Any]) -> dict[str, typing.Any]: + def __flatten_scenarios(cls, values: typing.Dict[str, typing.Any]) -> typing.Dict[str, typing.Any]: """Name certain scenarios.""" scenario_ids = { field.json_schema_extra["scenario_id"]: name diff --git a/genshin/models/genshin/teapot.py b/genshin/models/genshin/teapot.py index 8e619d00..6b6a273c 100644 --- a/genshin/models/genshin/teapot.py +++ b/genshin/models/genshin/teapot.py @@ -51,7 +51,7 @@ class TeapotReplica(APIModel): post_id: str title: str content: str - images: list[str] = Aliased("imgs") + images: typing.List[str] = Aliased("imgs") created_at: DateTimeField stats: TeapotReplicaStats lang: str # type: ignore @@ -61,7 +61,7 @@ class TeapotReplica(APIModel): view_type: int sub_type: int blueprint: TeapotReplicaBlueprint - video: str | None + video: typing.Optional[str] has_more_content: bool token: str @@ -71,7 +71,7 @@ def __extract_urls(cls, images: typing.Sequence[typing.Any]) -> typing.Sequence[ return [image if isinstance(image, str) else image["url"] for image in images] @pydantic.field_validator("video", mode="before") - def __extract_url(cls, video: typing.Any) -> str | None: + def __extract_url(cls, video: typing.Any) -> typing.Optional[str]: if isinstance(video, str): return video diff --git a/genshin/models/genshin/wiki.py b/genshin/models/genshin/wiki.py index 29f80c4b..fa9be4bb 100644 --- a/genshin/models/genshin/wiki.py +++ b/genshin/models/genshin/wiki.py @@ -37,7 +37,7 @@ class BaseWikiPreview(APIModel, Unique): name: str @pydantic.model_validator(mode="before") - def __unpack_filter_values(cls, values: dict[str, typing.Any]) -> dict[str, typing.Any]: + def __unpack_filter_values(cls, values: typing.Dict[str, typing.Any]) -> typing.Dict[str, typing.Any]: filter_values = { key.split("_", 1)[1]: value["values"][0] for key, value in values.get("filter_values", {}).items() @@ -47,7 +47,7 @@ def __unpack_filter_values(cls, values: dict[str, typing.Any]) -> dict[str, typi return values @pydantic.model_validator(mode="before") - def __flatten_display_field(cls, values: dict[str, typing.Any]) -> dict[str, typing.Any]: + def __flatten_display_field(cls, values: typing.Dict[str, typing.Any]) -> typing.Dict[str, typing.Any]: values.update(values.get("display_field", {})) return values @@ -104,7 +104,7 @@ class ArtifactPreview(BaseWikiPreview): effects: typing.Mapping[int, str] @pydantic.model_validator(mode="before") - def __group_effects(cls, values: dict[str, typing.Any]) -> dict[str, typing.Any]: + def __group_effects(cls, values: typing.Dict[str, typing.Any]) -> typing.Dict[str, typing.Any]: effects = { 1: values["single_set_effect"], 2: values["two_set_effect"], @@ -124,7 +124,7 @@ def __parse_drop_materials(cls, value: typing.Union[str, typing.Sequence[str]]) return json.loads(value) if isinstance(value, str) else value -_ENTRY_PAGE_MODELS: typing.Mapping[WikiPageType, type[BaseWikiPreview]] = { +_ENTRY_PAGE_MODELS: typing.Mapping[WikiPageType, typing.Type[BaseWikiPreview]] = { WikiPageType.CHARACTER: CharacterPreview, WikiPageType.WEAPON: WeaponPreview, WikiPageType.ARTIFACT: ArtifactPreview, @@ -147,14 +147,14 @@ class WikiPage(APIModel): @pydantic.field_validator("modules", mode="before") def __format_modules( cls, - value: typing.Union[list[dict[str, typing.Any]], dict[str, typing.Any]], - ) -> dict[str, typing.Any]: + value: typing.Union[typing.List[typing.Dict[str, typing.Any]], typing.Dict[str, typing.Any]], + ) -> typing.Dict[str, typing.Any]: if isinstance(value, typing.Mapping): return value - modules: dict[str, dict[str, typing.Any]] = {} + modules: typing.Dict[str, typing.Dict[str, typing.Any]] = {} for module in value: - components: dict[str, dict[str, typing.Any]] = { + components: typing.Dict[str, typing.Dict[str, typing.Any]] = { component["component_id"]: json.loads(component["data"] or "{}") for component in module["components"] } diff --git a/genshin/models/honkai/chronicle/battlesuits.py b/genshin/models/honkai/chronicle/battlesuits.py index 7dc0ecf0..677ca4e2 100644 --- a/genshin/models/honkai/chronicle/battlesuits.py +++ b/genshin/models/honkai/chronicle/battlesuits.py @@ -46,7 +46,7 @@ class FullBattlesuit(battlesuit.Battlesuit): stigmata: typing.Sequence[Stigma] = Aliased("stigmatas") @pydantic.model_validator(mode="before") - def __unnest_char_data(cls, values: dict[str, typing.Any]) -> dict[str, typing.Any]: + def __unnest_char_data(cls, values: typing.Dict[str, typing.Any]) -> typing.Dict[str, typing.Any]: if isinstance(values.get("character"), typing.Mapping): values.update(values["character"]) diff --git a/genshin/models/honkai/chronicle/modes.py b/genshin/models/honkai/chronicle/modes.py index 3d7c50a1..4f50462d 100644 --- a/genshin/models/honkai/chronicle/modes.py +++ b/genshin/models/honkai/chronicle/modes.py @@ -13,7 +13,7 @@ __all__ = ["ELF", "Boss", "ElysianRealm", "MemorialArena", "MemorialBattle", "OldAbyss", "SuperstringAbyss"] -REMEMBRANCE_SIGILS: dict[int, tuple[str, int]] = { +REMEMBRANCE_SIGILS: typing.Dict[int, typing.Tuple[str, int]] = { 119301: ("The MOTH Insignia", 1), 119302: ("Home Lost", 1), 119303: ("False Hope", 1), @@ -90,7 +90,7 @@ class ELF(APIModel, Unique): upgrade_level: int = Aliased("star") @pydantic.field_validator("rarity", mode="before") - def __fix_rank(cls, rarity: int | str) -> str: + def __fix_rank(cls, rarity: typing.Union[int, str]) -> str: if isinstance(rarity, str): return rarity @@ -122,7 +122,7 @@ class BaseAbyss(APIModel): score: int lineup: typing.Sequence[battlesuit.Battlesuit] boss: Boss - elf: ELF | None + elf: typing.Optional[ELF] class OldAbyss(BaseAbyss): @@ -180,7 +180,7 @@ class MemorialBattle(APIModel): score: int lineup: typing.Sequence[battlesuit.Battlesuit] - elf: ELF | None + elf: typing.Optional[ELF] boss: Boss @@ -278,7 +278,7 @@ class ElysianRealm(APIModel): signets: typing.Sequence[Signet] = Aliased("buffs") leader: battlesuit.Battlesuit = Aliased("main_avatar") supports: typing.Sequence[battlesuit.Battlesuit] = Aliased("support_avatars") - elf: ELF | None + elf: typing.Optional[ELF] remembrance_sigil: RemembranceSigil = Aliased("extra_item_icon") @pydantic.field_validator("remembrance_sigil", mode="before") diff --git a/genshin/models/honkai/chronicle/stats.py b/genshin/models/honkai/chronicle/stats.py index b7ab00b3..a30c18f9 100644 --- a/genshin/models/honkai/chronicle/stats.py +++ b/genshin/models/honkai/chronicle/stats.py @@ -100,7 +100,7 @@ class HonkaiStats(APIModel): elysian_realm: ElysianRealmStats = Aliased() @pydantic.model_validator(mode="before") - def __pack_gamemode_stats(cls, values: dict[str, typing.Any]) -> dict[str, typing.Any]: + def __pack_gamemode_stats(cls, values: typing.Dict[str, typing.Any]) -> typing.Dict[str, typing.Any]: if "new_abyss" in values: values["abyss"] = SuperstringAbyssStats(**values["new_abyss"], **values) elif "old_abyss" in values: diff --git a/genshin/models/honkai/constants.py b/genshin/models/honkai/constants.py index 8e6b28fd..3f644fa4 100644 --- a/genshin/models/honkai/constants.py +++ b/genshin/models/honkai/constants.py @@ -6,7 +6,7 @@ # TODO: Make this more dynamic # fmt: off -BATTLESUIT_IDENTIFIERS: dict[int, str] = { +BATTLESUIT_IDENTIFIERS: typing.Dict[int, str] = { 101: "KianaC2", 102: "KianaC1", 103: "KianaC4", diff --git a/genshin/models/hoyolab/record.py b/genshin/models/hoyolab/record.py index 536944e8..00f46454 100644 --- a/genshin/models/hoyolab/record.py +++ b/genshin/models/hoyolab/record.py @@ -117,8 +117,8 @@ class HoyolabUserCertification(APIModel): For example artist's type is 2. """ - icon_url: str | None = None - description: str | None = Aliased("desc", default=None) + icon_url: typing.Optional[str] = None + description: typing.Optional[str] = Aliased("desc", default=None) type: int @@ -138,11 +138,11 @@ class FullHoyolabUser(PartialHoyolabUser): Not actually full, but most of the data is useless. """ - certification: HoyolabUserCertification | None = None - level: HoyolabUserLevel | None = None + certification: typing.Optional[HoyolabUserCertification] = None + level: typing.Optional[HoyolabUserLevel] = None pendant_url: str = Aliased("pendant") - bg_url: str | None = None - pc_bg_url: str | None = None + bg_url: typing.Optional[str] = None + pc_bg_url: typing.Optional[str] = None class RecordCard(GenshinAccount): @@ -176,7 +176,7 @@ def __new__(cls, **kwargs: typing.Any) -> RecordCard: has_uid: bool = Aliased("has_role") url: str - def as_dict(self) -> dict[str, typing.Any]: + def as_dict(self) -> typing.Dict[str, typing.Any]: """Return data as a dictionary.""" return {d.name: (int(d.value) if d.value.isdigit() else d.value) for d in self.data} diff --git a/genshin/models/model.py b/genshin/models/model.py index a60fb4f6..b2742263 100644 --- a/genshin/models/model.py +++ b/genshin/models/model.py @@ -33,7 +33,7 @@ def __hash__(self) -> int: def Aliased( - alias: str | None = None, + alias: typing.Optional[str] = None, default: typing.Any = None, **kwargs: typing.Any, ) -> typing.Any: diff --git a/genshin/models/starrail/chronicle/challenge.py b/genshin/models/starrail/chronicle/challenge.py index b9678a5c..81ac637b 100644 --- a/genshin/models/starrail/chronicle/challenge.py +++ b/genshin/models/starrail/chronicle/challenge.py @@ -1,6 +1,6 @@ """Starrail chronicle challenge.""" -from typing import Any, Optional +from typing import Any, Dict, List, Optional import pydantic @@ -30,7 +30,7 @@ class FloorNode(APIModel): """Node for a memory of chaos floor.""" challenge_time: PartialTime - avatars: list[FloorCharacter] + avatars: List[FloorCharacter] class StarRailChallengeFloor(APIModel): @@ -74,13 +74,13 @@ class StarRailChallenge(APIModel): total_battles: int = Aliased("battle_num") has_data: bool - floors: list[StarRailFloor] = Aliased("all_floor_detail") - seasons: list[StarRailChallengeSeason] = Aliased("groups") + floors: List[StarRailFloor] = Aliased("all_floor_detail") + seasons: List[StarRailChallengeSeason] = Aliased("groups") @pydantic.model_validator(mode="before") - def __extract_name(cls, values: dict[str, Any]) -> dict[str, Any]: - if "groups" in values and isinstance(values["groups"], list): - seasons: list[dict[str, Any]] = values["groups"] + def __extract_name(cls, values: Dict[str, Any]) -> Dict[str, Any]: + if "groups" in values and isinstance(values["groups"], List): + seasons: List[Dict[str, Any]] = values["groups"] if len(seasons) > 0: values["name"] = seasons[0]["name_mi18n"] @@ -129,14 +129,14 @@ class StarRailPureFiction(APIModel): total_battles: int = Aliased("battle_num") has_data: bool - floors: list[FictionFloor] = Aliased("all_floor_detail") - seasons: list[StarRailChallengeSeason] = Aliased("groups") + floors: List[FictionFloor] = Aliased("all_floor_detail") + seasons: List[StarRailChallengeSeason] = Aliased("groups") max_floor_id: int @pydantic.model_validator(mode="before") - def __unnest_groups(cls, values: dict[str, Any]) -> dict[str, Any]: - if "groups" in values and isinstance(values["groups"], list): - seasons: list[dict[str, Any]] = values["groups"] + def __unnest_groups(cls, values: Dict[str, Any]) -> Dict[str, Any]: + if "groups" in values and isinstance(values["groups"], List): + seasons: List[Dict[str, Any]] = values["groups"] if len(seasons) > 0: values["name"] = seasons[0]["name_mi18n"] values["season_id"] = seasons[0]["schedule_id"] @@ -196,6 +196,6 @@ class StarRailAPCShadow(APIModel): total_battles: int = Aliased("battle_num") has_data: bool - floors: list[APCShadowFloor] = Aliased("all_floor_detail") - seasons: list[APCShadowSeason] = Aliased("groups") + floors: List[APCShadowFloor] = Aliased("all_floor_detail") + seasons: List[APCShadowSeason] = Aliased("groups") max_floor_id: int diff --git a/genshin/models/starrail/chronicle/notes.py b/genshin/models/starrail/chronicle/notes.py index cfc26d04..08ef7dfd 100644 --- a/genshin/models/starrail/chronicle/notes.py +++ b/genshin/models/starrail/chronicle/notes.py @@ -11,7 +11,7 @@ class StarRailExpedition(APIModel): """StarRail expedition.""" - avatars: list[str] + avatars: typing.List[str] status: typing.Literal["Ongoing", "Finished"] remaining_time: datetime.timedelta name: str diff --git a/genshin/models/starrail/chronicle/rogue.py b/genshin/models/starrail/chronicle/rogue.py index a3882b36..ea2b5329 100644 --- a/genshin/models/starrail/chronicle/rogue.py +++ b/genshin/models/starrail/chronicle/rogue.py @@ -1,5 +1,6 @@ """Starrail Rogue models.""" +from typing import List from genshin.models.model import APIModel @@ -53,7 +54,7 @@ class RogueBuff(APIModel): """Rogue buff info.""" base_type: RogueBuffType - items: list[RogueBuffItem] + items: List[RogueBuffItem] class RogueMiracle(APIModel): @@ -70,11 +71,11 @@ class RogueRecordDetail(APIModel): name: str finish_time: PartialTime score: int - final_lineup: list[RogueCharacter] - base_type_list: list[RogueBuffType] - cached_avatars: list[RogueCharacter] - buffs: list[RogueBuff] - miracles: list[RogueMiracle] + final_lineup: List[RogueCharacter] + base_type_list: List[RogueBuffType] + cached_avatars: List[RogueCharacter] + buffs: List[RogueBuff] + miracles: List[RogueMiracle] difficulty: int progress: int @@ -83,7 +84,7 @@ class RogueRecord(APIModel): """generic record data.""" basic: RogueRecordBasic - records: list[RogueRecordDetail] + records: List[RogueRecordDetail] has_data: bool diff --git a/genshin/models/zzz/chronicle/challenge.py b/genshin/models/zzz/chronicle/challenge.py index 5acf8e19..4ccaab9c 100644 --- a/genshin/models/zzz/chronicle/challenge.py +++ b/genshin/models/zzz/chronicle/challenge.py @@ -70,18 +70,18 @@ def __convert_weakness(cls, v: int) -> typing.Union[ZZZElementType, int]: class ShiyuDefenseNode(APIModel): """Shiyu Defense node model.""" - characters: list[ShiyuDefenseCharacter] = Aliased("avatars") + characters: typing.List[ShiyuDefenseCharacter] = Aliased("avatars") bangboo: ShiyuDefenseBangboo = Aliased("buddy") - recommended_elements: list[ZZZElementType] = Aliased("element_type_list") - enemies: list[ShiyuDefenseMonster] = Aliased("monster_info") + recommended_elements: typing.List[ZZZElementType] = Aliased("element_type_list") + enemies: typing.List[ShiyuDefenseMonster] = Aliased("monster_info") @pydantic.field_validator("enemies", mode="before") @classmethod def __convert_enemies( - cls, value: dict[typing.Literal["level", "list"], typing.Any] - ) -> list[ShiyuDefenseMonster]: + cls, value: typing.Dict[typing.Literal["level", "list"], typing.Any] + ) -> typing.List[ShiyuDefenseMonster]: level = value["level"] - result: list[ShiyuDefenseMonster] = [] + result: typing.List[ShiyuDefenseMonster] = [] for monster in value["list"]: monster["level"] = level result.append(ShiyuDefenseMonster(**monster)) @@ -94,7 +94,7 @@ class ShiyuDefenseFloor(APIModel): index: int = Aliased("layer_index") rating: typing.Literal["S", "A", "B"] id: int = Aliased("layer_id") - buffs: list[ShiyuDefenseBuff] + buffs: typing.List[ShiyuDefenseBuff] node_1: ShiyuDefenseNode node_2: ShiyuDefenseNode challenge_time: DateTimeField = Aliased("floor_challenge_time") @@ -116,7 +116,7 @@ class ShiyuDefense(APIModel): end_time: typing.Optional[DateTimeField] = Aliased("hadal_end_time") has_data: bool ratings: typing.Mapping[typing.Literal["S", "A", "B"], int] = Aliased("rating_list") - floors: list[ShiyuDefenseFloor] = Aliased("all_floor_detail") + floors: typing.List[ShiyuDefenseFloor] = Aliased("all_floor_detail") fastest_clear_time: int = Aliased("fast_layer_time") """Fastest clear time this season in seconds.""" max_floor: int = Aliased("max_layer") @@ -131,6 +131,6 @@ def __parse_datetime(cls, value: typing.Mapping[str, typing.Any]) -> typing.Opti @pydantic.field_validator("ratings", mode="before") @classmethod def __convert_ratings( - cls, v: list[dict[typing.Literal["times", "rating"], typing.Any]] + cls, v: typing.List[typing.Dict[typing.Literal["times", "rating"], typing.Any]] ) -> typing.Mapping[typing.Literal["S", "A", "B"], int]: return {d["rating"]: d["times"] for d in v} diff --git a/genshin/models/zzz/chronicle/notes.py b/genshin/models/zzz/chronicle/notes.py index 090159bc..d2dafb88 100644 --- a/genshin/models/zzz/chronicle/notes.py +++ b/genshin/models/zzz/chronicle/notes.py @@ -37,7 +37,7 @@ def full_datetime(self) -> datetime.datetime: return datetime.datetime.now().astimezone() + datetime.timedelta(seconds=self.seconds_till_full) @pydantic.model_validator(mode="before") - def __unnest_progress(cls, values: dict[str, typing.Any]) -> dict[str, typing.Any]: + def __unnest_progress(cls, values: typing.Dict[str, typing.Any]) -> typing.Dict[str, typing.Any]: return {**values, **values.pop("progress")} @@ -61,6 +61,6 @@ def __transform_value(cls, v: typing.Literal["CardSignDone", "CardSignNotDone"]) return v == "CardSignDone" @pydantic.model_validator(mode="before") - def __unnest_value(cls, values: dict[str, typing.Any]) -> dict[str, typing.Any]: + def __unnest_value(cls, values: typing.Dict[str, typing.Any]) -> typing.Dict[str, typing.Any]: values["video_store_state"] = values["vhs_sale"]["sale_state"] return values diff --git a/genshin/paginators/api.py b/genshin/paginators/api.py index 580b29bd..8201a84f 100644 --- a/genshin/paginators/api.py +++ b/genshin/paginators/api.py @@ -30,7 +30,7 @@ async def __call__(self, page: int, /) -> typing.Sequence[T_co]: class TokenGetterCallback(typing.Protocol[T_co]): """Callback for returning resources based on a page or cursor.""" - async def __call__(self, token: str, /) -> tuple[str, typing.Sequence[T_co]]: + async def __call__(self, token: str, /) -> typing.Tuple[str, typing.Sequence[T_co]]: """Return a sequence of resources.""" ... @@ -55,18 +55,18 @@ class PagedPaginator(typing.Generic[T], APIPaginator[T]): getter: GetterCallback[T] """Underlying getter that yields the next page.""" - _page_size: int | None + _page_size: typing.Optional[int] """Expected non-zero page size to be able to tell the end.""" - current_page: int | None + current_page: typing.Optional[int] """Current page counter..""" def __init__( self, getter: GetterCallback[T], *, - limit: int | None = None, - page_size: int | None = None, + limit: typing.Optional[int] = None, + page_size: typing.Optional[int] = None, ) -> None: super().__init__(limit=limit) self.getter = getter @@ -74,7 +74,7 @@ def __init__( self.current_page = 1 - async def next_page(self) -> typing.Iterable[T] | None: + async def next_page(self) -> typing.Optional[typing.Iterable[T]]: """Get the next page of the paginator.""" if self.current_page is None: return None @@ -101,17 +101,17 @@ class TokenPaginator(typing.Generic[T], APIPaginator[T]): getter: TokenGetterCallback[T] """Underlying getter that yields the next page.""" - _page_size: int | None + _page_size: typing.Optional[int] """Expected non-zero page size to be able to tell the end.""" - token: str | None + token: typing.Optional[str] def __init__( self, getter: TokenGetterCallback[T], *, - limit: int | None = None, - page_size: int | None = None, + limit: typing.Optional[int] = None, + page_size: typing.Optional[int] = None, ) -> None: super().__init__(limit=limit) self.getter = getter @@ -119,7 +119,7 @@ def __init__( self.token = "" - async def next_page(self) -> typing.Iterable[T] | None: + async def next_page(self) -> typing.Optional[typing.Iterable[T]]: """Get the next page of the paginator.""" if self.token is None: return None @@ -145,19 +145,19 @@ class CursorPaginator(typing.Generic[UniqueT], APIPaginator[UniqueT]): getter: GetterCallback[UniqueT] """Underlying getter that yields the next page.""" - _page_size: int | None + _page_size: typing.Optional[int] """Expected non-zero page size to be able to tell the end.""" - end_id: int | None + end_id: typing.Optional[int] """Current end id. If none then exhausted.""" def __init__( self, getter: GetterCallback[UniqueT], *, - limit: int | None = None, + limit: typing.Optional[int] = None, end_id: int = 0, - page_size: int | None = 20, + page_size: typing.Optional[int] = 20, ) -> None: super().__init__(limit=limit) self.getter = getter @@ -165,7 +165,7 @@ def __init__( self._page_size = page_size - async def next_page(self) -> typing.Iterable[UniqueT] | None: + async def next_page(self) -> typing.Optional[typing.Iterable[UniqueT]]: """Get the next page of the paginator.""" if self.end_id is None: return None diff --git a/genshin/paginators/base.py b/genshin/paginators/base.py index f9cb8591..a466c59a 100644 --- a/genshin/paginators/base.py +++ b/genshin/paginators/base.py @@ -102,7 +102,7 @@ class BasicPaginator(typing.Generic[T], Paginator[T], abc.ABC): iterator: typing.AsyncIterator[T] """Underlying iterator.""" - def __init__(self, iterable: typing.Iterable[T] | typing.AsyncIterable[T]) -> None: + def __init__(self, iterable: typing.Union[typing.Iterable[T], typing.AsyncIterable[T]]) -> None: if isinstance(iterable, typing.AsyncIterable): self.iterator = iterable.__aiter__() else: @@ -120,16 +120,16 @@ class BufferedPaginator(typing.Generic[T], Paginator[T], abc.ABC): __slots__ = ("limit", "_buffer", "_counter") - limit: int | None + limit: typing.Optional[int] """Limit of items to be yielded.""" - _buffer: typing.Iterator[T] | None + _buffer: typing.Optional[typing.Iterator[T]] """Item buffer. If none then exhausted.""" _counter: int """Amount of yielded items so far. No guarantee to be synchronized.""" - def __init__(self, *, limit: int | None = None) -> None: + def __init__(self, *, limit: typing.Optional[int] = None) -> None: self.limit = limit self._buffer = iter(()) @@ -147,7 +147,7 @@ def _complete(self) -> typing.NoReturn: raise # pyright bug @abc.abstractmethod - async def next_page(self) -> typing.Iterable[T] | None: + async def next_page(self) -> typing.Optional[typing.Iterable[T]]: """Get the next page of the paginator.""" async def __anext__(self) -> T: @@ -185,16 +185,16 @@ class MergedPaginator(typing.Generic[T], Paginator[T]): Only used as pointers to a heap. """ - _heap: list[tuple[typing.Any, int, T, typing.AsyncIterator[T]]] + _heap: typing.List[typing.Tuple[typing.Any, int, T, typing.AsyncIterator[T]]] """Underlying heap queue. List of (comparable, unique order id, value, iterator) """ - limit: int | None + limit: typing.Optional[int] """Limit of items to be yielded""" - _key: typing.Callable[[T], typing.Any] | None + _key: typing.Optional[typing.Callable[[T], typing.Any]] """Sorting key.""" _prepared: bool @@ -207,8 +207,8 @@ def __init__( self, iterables: typing.Collection[typing.AsyncIterable[T]], *, - key: typing.Callable[[T], typing.Any] | None = None, - limit: int | None = None, + key: typing.Optional[typing.Callable[[T], typing.Any]] = None, + limit: typing.Optional[int] = None, ) -> None: self.iterators = [iterable.__aiter__() for iterable in iterables] self._key = key @@ -230,8 +230,8 @@ def _create_heap_item( self, value: T, iterator: typing.AsyncIterator[T], - order: int | None = None, - ) -> tuple[typing.Any, int, T, typing.AsyncIterator[T]]: + order: typing.Optional[int] = None, + ) -> typing.Tuple[typing.Any, int, T, typing.AsyncIterator[T]]: """Create a new item for the heap queue.""" sort_value = self._key(value) if self._key else value if order is None: diff --git a/genshin/utility/auth.py b/genshin/utility/auth.py index 5634f725..c0de46e4 100644 --- a/genshin/utility/auth.py +++ b/genshin/utility/auth.py @@ -152,12 +152,12 @@ def encrypt_credentials(text: str, key_type: typing.Literal[1, 2]) -> str: return base64.b64encode(crypto).decode("utf-8") -def get_aigis_header(session_id: str, mmt_data: dict[str, typing.Any]) -> str: +def get_aigis_header(session_id: str, mmt_data: typing.Dict[str, typing.Any]) -> str: """Get aigis header.""" return f"{session_id};{base64.b64encode(json.dumps(mmt_data).encode()).decode()}" -def generate_sign(data: dict[str, typing.Any], key: str) -> str: +def generate_sign(data: typing.Dict[str, typing.Any], key: str) -> str: """Generate a sign for the given `data` and `app_key`.""" string = "" for k in sorted(data.keys()): diff --git a/genshin/utility/concurrency.py b/genshin/utility/concurrency.py index 6b1894b6..681a5cf5 100644 --- a/genshin/utility/concurrency.py +++ b/genshin/utility/concurrency.py @@ -20,7 +20,7 @@ def prevent_concurrency(func: CallableT) -> CallableT: """ def wrapper(func: AnyCallable) -> AnyCallable: - lock: asyncio.Lock | None = None + lock: typing.Optional[asyncio.Lock] = None @functools.wraps(func) async def inner(*args: typing.Any, **kwargs: typing.Any) -> typing.Any: @@ -48,7 +48,7 @@ def __init__( method: AnyCallable, decorator: typing.Callable[[AnyCallable], AnyCallable], *, - name: str | None = None, + name: typing.Optional[str] = None, ) -> None: self.method = method # type: ignore # mypy doesn't understand methods self.decorator = decorator # type: ignore # mypy doesn't understand methods @@ -57,7 +57,7 @@ def __init__( def __set_name__(self, owner: type, name: str) -> None: self.name = name - def __get__(self, instance: T | None, owner: type[T]) -> AnyCallable: + def __get__(self, instance: typing.Optional[T], owner: typing.Type[T]) -> AnyCallable: if instance is None: return self.method diff --git a/genshin/utility/ds.py b/genshin/utility/ds.py index 7a95a3aa..d9a47951 100644 --- a/genshin/utility/ds.py +++ b/genshin/utility/ds.py @@ -47,7 +47,7 @@ def get_ds_headers( data: typing.Any = None, params: typing.Optional[typing.Mapping[str, typing.Any]] = None, lang: typing.Optional[str] = None, -) -> dict[str, typing.Any]: +) -> typing.Dict[str, typing.Any]: """Get ds http headers.""" if region == types.Region.OVERSEAS: ds_headers = { diff --git a/genshin/utility/logfile.py b/genshin/utility/logfile.py index 50f0e8c8..f0b6719b 100644 --- a/genshin/utility/logfile.py +++ b/genshin/utility/logfile.py @@ -49,7 +49,7 @@ def get_output_log(*, game: typing.Optional[types.Game] = None) -> pathlib.Path: """Get output_log.txt for a game.""" locallow = pathlib.Path("~/AppData/LocalLow").expanduser() - game_name: list[str] = [] + game_name: typing.List[str] = [] if game is None or game == types.Game.GENSHIN: game_name += ["Genshin Impact", "原神"] if game is None or game == types.Game.STARRAIL: @@ -70,7 +70,7 @@ def get_output_log(*, game: typing.Optional[types.Game] = None) -> pathlib.Path: def _expand_game_location(game_location: pathlib.Path, *, game: typing.Optional[types.Game] = None) -> pathlib.Path: """Expand a game location folder to data_2.""" - data_location: list[pathlib.Path] = [] + data_location: typing.List[pathlib.Path] = [] if "Data" in str(game_location): while "Data" not in game_location.name: game_location = game_location.parent diff --git a/tests/conftest.py b/tests/conftest.py index 66c6d9a4..3b2b502b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -209,7 +209,7 @@ def pytest_addoption(parser: pytest.Parser): parser.addoption("--cooperative", action="store_true") -def pytest_collection_modifyitems(items: list[pytest.Item], config: pytest.Config): +def pytest_collection_modifyitems(items: typing.List[pytest.Item], config: pytest.Config): if config.option.cooperative: for item in items: if isinstance(item, pytest.Function) and asyncio.iscoroutinefunction(item.obj):